中国剩余定理,枚举(数论难题,UVA 11754)

    xiaoxiao2021-11-19  45

    就是给你C个同余方程组,每个方程组有Kc个模方程。一个同余方程组的解是其中每个模方程的解的并集,所有同余方程组的解是单个同余方程组的解的交集。求最小的S个解。

    无非就是枚举方程的组合,然后跑中国剩余定理或者从小到大枚举x判断是否可行。但是当时觉得都很慢。没有发现互补的一面。

    然后就想用一些数论的方法把一个方程组变成单个方程,改编一下china函数的算法,然后发现没办法做到,然后卒。

    事实上枚举x是有技巧的,不是全部枚举一遍,再全部判断一遍。你可以把一些本来用于判断的条件用来当做枚举的限制条件,再用剩下的条件来判断是否可行。对半分为最好。但出于编程方便,一般选一个限制做多的条件就足够了。在这题里,选k/x最小的那个,那么k相对小,x相对大。这样枚举答案tx+yi时,tx增长很快,yi也不用枚举很多。因此,枚举量相对较小。

    至于枚举方程的组合,当数据小的时候速度还不错,但数据一大就爆炸。

    大白书上说:

    if(tot>10000) solve_enum(p); else solve_china();

    数据大时铁定不能用solve_china();只好用solve_enum(p);啦。 但最难理解的就是为什么数据小时不能用solve_enum(p);而只能用solve_china();

    答案就是这两种方法的目标与时间复杂度是不同的。

    solve_enum(p);只找最小的S个解,是多项式的时间复杂度。

    而solve_china();是找所有可行解,是指数型的时间复杂度。

    两者的增长速度是不一样的。

    当数据量很大时,solve_china();的时间复杂度将远远超过solve_enum(p);因为可行解太多了,而我们又不需要那么多的可行解,我们只要最小的S个就好了不是吗?这就造成了极大地浪费。

    而在数据量很小的时候,solve_enum(p);毕竟是枚举,要迭代很多次的,而且大部分的枚举都是错误的尝试。而此时solve_china();只找可行解,而可行解很少。所以solve_china();会快很多。

    然后就是关于枚举方程的组合,可以用dfs,思路清晰,代码实现也方便。

    用位向量的话有多少位也不确定。或者搞一大堆循环来枚举也不知道要多少层。还是dfs最好。

    然后枚举Y要从小到大,因为Y<X所以先枚举t,然后再从小到大枚举y,保证x递增。

    然后用solve_china();找到的解都是剩余系里的,先排个序,再输出。

    如果不够用,就要加上M再输出一遍,直到够为止,显然输出的结果都是递增的。

    还有就是0不能是解。

    代码

    #include<bits/stdc++.h> using namespace std; typedef long long ll; ll C,S; ll X[10],K[10]; ll Y[10][110]; void ex_gcd(ll a,ll b,ll& d,ll& x,ll& y) { if(!b){d=a;x=1;y=0;} else {ex_gcd(b,a%b,d,y,x);y-=x*(a/b);} } ll china(ll n,ll* a,ll* m) { ll M=1,d,x=0,y; for(ll i=0;i<n;i++) M*=m[i]; for(ll i=0;i<n;i++) { ll w=M/m[i]; ex_gcd(m[i],w,d,d,y); x=(x+y*w*a[i])%M; } return (x+M)%M; } void solve_enum(ll p) { set<ll>s[10]; for(ll c=0;c<C;c++) if(c!=p) for(ll i=0;i<K[c];i++) s[c].insert(Y[c][i]); for(ll t=0;S;t++) for(ll i=0;i<K[p];i++) { ll ans=t*X[p]+Y[p][i]; if(!ans) continue; bool ok=true; for(ll c=0;c<C;c++) if(c!=p) if(!s[c].count(ans%X[c])) { ok=false; break; } if(ok) { printf("%lld\n",ans); if(--S==0) break; } } } vector<ll>vec; ll a[10]; void dfs(ll d) { if(d==C) vec.push_back(china(d,a,X)); else for(ll i=0;i<K[d];i++) { a[d]=Y[d][i]; dfs(d+1); } } void solve_china() { vec.clear(); dfs(0); sort(vec.begin(),vec.end()); ll M=1; for(ll i=0;i<C;i++) M*=X[i]; for(ll i=0;S;i++) for(unsigned int j=0;j<vec.size();j++) { ll ans=vec[j]+i*M; if(!ans) continue; printf("%lld\n",ans); if(--S==0) break; } } int main() { while(scanf("%lld %lld",&C,&S)==2&&C&&S) { ll tot=1; ll p=0; for(ll c=0;c<C;c++) { scanf("%lld %lld",&X[c],&K[c]); tot*=K[c]; if(K[p]*X[c]>K[c]*X[p]) p=c; for(ll i=0;i<K[c];i++) scanf("%lld",&Y[c][i]); sort(Y[c],Y[c]+K[c]); } if(tot>10000) solve_enum(p); else solve_china(); puts(""); } return 0; }

    转载请注明原文地址: https://ju.6miu.com/read-678359.html

    最新回复(0)