给你 C 个约束条件,你需要得出 S 个最小的解. 每一个约束条件可以表现为他的某一个ans ,满足 ans mod X = Yi 的形式,
思路:
中国剩余定理: (维基百科) 这里我们可以在选取了每个条件中的余数后用中国剩余定理解决, 那么问题是我们怎么样选出最小的 S 个. 因为我们在选取余数的时候用的是深搜,但是这样如果数据太大很容易超时,那么我们设定一个限制条件,当余数的数量大于某个值的时候,我们就用其他方法, 这个时候我们可以用 X / K 最小的一组开始枚举 它所能组成的所有的数,然后判断这个数,是否符合其他 的条件,如果符合就输出.
另一个情况就是余数个数比较少的情况,那么此时我们可以暴力枚举所有余数的组合情况,然后用中国剩余定理解出ans.
中国剩余定理:
对于上面的式子,我们要解出 x 的值,
令 M = m1 * m2 * m3 *m4…mn;
然后我们设Mi = M / mi ;
然后取 ti 为 Mi 的关于 mi 逆元(这里求逆元我们用扩展欧几里得算法),
最后我们得出通解: x = $\sum_{i = 1} ^ n a_i * t_i * M_i $ + k * M, k $\in $ Z,
typedeflonglong LL; LL x[20]; int k[20]; int c, s; int a[105]; vector<int > ans; int num; int y[20][105];
voidexgcd(LL mi, LL xi, LL &e, LL &d){ if(!xi){ e = 1; d = 0; }else { exgcd(xi, mi % xi, d, e); d-= mi / xi * e; } }
LL china(){//中国剩余定理 LL m = 1; LL cnm = 0; LL e, d; for(int i = 1; i <= c; ++i) m *= x[i]; for(int i = 1; i <= c;++i){ LL mi = m / x[i]; exgcd(mi, x[i], e, d);//扩展欧几里得求逆元 cnm = (cnm + e * mi * a[i] ) % m; } return (cnm + m) % m; }
voiddfs(int cur){ if(cur == c + 1){ LL tmp = china(); ans.push_back(tmp); return ; } for(int i = 1; i <= k[cur];++i){ a[cur] = y[cur][i]; dfs(cur+1); } }
voidsolvechina(){ LL m = 1; ans.clear(); dfs(1); sort(ans.begin(), ans.end()); for(int i = 1; i <= c;++i) m *= x[i]; for(int i = 0; s; ++i) for(int j = 0; j < ans.size();++j){ LL ansk = m * i +ans[j]; if(ansk > 0){ printf("%lld\n",ansk); --s; if(s == 0) break; } } }
set<int > v[20]; voidsolvenum(int cur){ for(int i = 1; i <= c; ++i) if(i != cur){ v[i].clear(); for(int j = 1; j <= k[i]; ++j) v[i].insert(y[i][j]); }for(int i = 0; s; ++i) for(int j = 1; j <= k[cur]; ++j){ LL m = (LL)i * x[cur] +y[cur][j];//从小到大枚举ans, if (!m) continue; bool flag = true; for(int l = 1; l <= c; ++l) if(l !=cur && !v[l].count(m % x[l])){//判断ans是否符合所有的情况, flag = false; break; }if(flag){ printf("%lld\n",m); --s; if(s == 0) break; } } }