数论基础1011 UVA 11754 剩余定理+枚举

本文探讨了一种针对中国剩余定理(CRT)的优化算法,适用于处理大量数据的情况。通过枚举和筛选策略,解决了传统CRT算法在面对大规模数据集时的时间复杂度过高的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
给C组数据,每组都满足的最小~第S小的数
每组数据有X,和k个R,如果N%X=任意一个R就表示满足这组数据
思路:
先用的CRT…TLE…(算了算…好爆炸的时间复杂度
如果是小数据的话,我们可以用CRT
如果是大数据就不行了,
因为数据很多,所以我们如果用枚举,随机到每个X[i]上的概率就比较高
我们可以找到最小的k[i]/X[i]来枚举t
因为可能数据弄出来不到S个,这时候我们就要手动添加大小,所以CRT里也要while,
别忘了最后的换行…

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long LL;
const int maxn = 15;
const int inf=(1<<28)-1;
int C,S;
LL Mod[maxn],k[maxn],r[maxn][105],R[15];
set<LL>Set[maxn];
vector<LL>Ans;
void Solve1(int pos)
{
    for(int i=1;i<=C;++i)
    if(i!=pos)
    {
        Set[i].clear();
        for(int j=1;j<=k[i];++j)
        Set[i].insert(r[i][j]);
    }
    int t=0;
    while(S)
    {
        for(int i=1;i<=k[pos];++i)
        {
            LL Now=t*Mod[pos]+r[pos][i];
            if(!Now) continue;
            bool flag=true;
            for(int j=1;j<=C;++j)
            if(j!=pos&&!Set[j].count(Now%Mod[j]))
                flag=false;
            if(flag)
            {
                printf("%lld\n",Now);
                if(--S==0) break;
            }
        }
        ++t;
    }
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)//扩展欧几里德求逆元
{
    if (b == 0)
        d = a, x = 1, y = 0;
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x * (a / b);
    }
}

LL CRT(LL a[],LL m[],LL n)//中国剩余定理求解
{
    LL aa = a[1];
    LL mm = m[1];
    for (int i=1; i<=n; i++)
    {
        LL sub = (a[i] - aa);
        LL d, x, y;
        exgcd(mm, m[i], d, x, y);
        if (sub % d) return -1;

        LL new_m = m[i]/d;
        new_m = (sub/d*x%new_m+new_m)%new_m;
        aa = mm*new_m+aa;
        mm = mm*m[i]/d;
    }
    aa = (aa+mm)%mm;
    return aa;
}

void dfs(int pos)
{
    if(pos==C+1)
    {
        Ans.push_back(CRT(R,Mod,C));
        return ;
    }
    for(int i=1;i<=k[pos];++i)
    {
        R[pos]=r[pos][i];
        dfs(pos+1);
    }
}
void Solve2()
{
    Ans.clear();
    dfs(1);
    sort(Ans.begin(),Ans.end());
    LL M=1,t=0;
    for(int i=1;i<=C;++i)
    M*=Mod[i];
    while(S)
    {
        int Size=Ans.size();
        for(int i=0;i<Size;++i)
        {
            LL Now=t*M+Ans[i];
            if(Now>0)
            {
                printf("%lld\n",Now);
                if(--S==0) break;
            }
        }
        ++t;
    }
}
int main()
{
    while(~scanf("%d%d",&C,&S)&&!(C==0&&S==0))
    {
        LL Sum=1;
        int pos=1;
        for(int i=1;i<=C;++i)
        {
            scanf("%lld%lld",&Mod[i],&k[i]);
            Sum*=k[i];
            for(int j=1;j<=k[i];++j)
                scanf("%lld",&r[i][j]);
            sort(r[i]+1,r[i]+k[i]+1);
            if(k[pos]*Mod[i]>k[i]*Mod[pos])
                pos=i;
        }
        if(Sum>10000) Solve1(pos);
        else Solve2(); 
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值