题意:对于C个条件,每个条件都有X和k个y,找出N,使得N对于每个条件,都有N%X == 某一个y,然后以递增次序输出S个满足的N。
Input consists of several test cases. Each test case starts with a line containing C, the number of clues(1 ≤ C ≤ 9), and S, the number of desired solutions (1 ≤ S ≤ 10). The next C lines each startwith two integers X (2 ≤ X) and k (1 ≤ k ≤ 100), followed by the k distinct integers Y1, Y2, . . . , Yk(0 ≤ Y1, Y2, . . . , Yk < X).
You may assume that the X’s in each test case are pairwise relatively prime (ie, they have nocommon factor except 1). Also, the product of the X’s will fit into a 32-bit integer.
The last test case is followed by a line containing two zeros.
题解:中国剩余定理+枚举
条件 N%X == yi 可以表示为 N ≡ yi (mod X),那么就可以用中国剩余定理求解最小N,但是会t,考虑两种情况。
用tot表示所有k的乘积,即余数的组合数。
①当tot较小时,可以dfs枚举y的每一种组合,然后跑crt,求出每种组合最小的N,递增输出S个即可。但可能不够,考虑C个X两两互质,那它们的乘积就是lcm,外层循环累加即可。
②当tot较大时,我们从C个条件中选择出最优的一个条件,根据这个最优条件,枚举商i生成N,N = i × X[best] + y[best][j],然后取模其他除数X得到r,看对应余数集合中能否有取模后到余数r。如果最优的X小的话,那么i必然要枚举更多次,故而X要尽量大,如果余数的数量k太多的话,枚举也会增多,故而k要尽量小,综合即是X/k要最大。
如果仅使用枚举的话,还是会t。注意y可能为0。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
int c, s, x[11], k[11], y[11][111], bestc;
ll tot;
int a[11]; //存储当前组合
vector<int> sol; //存储每个组合的可行N
set<int> vis[15]; //存储除bestc外的余数yi
void gcd(ll a, ll b, ll& d, ll& x, ll& y) {
if (!b) {
d = a;
x = 1;
y = 0;
}
else {
gcd(b, a % b, d, y, x);
y -= x * (a / b);
}
}
int china(int n, int* s, int* m) {
ll M = 1, d, y, x = 0;
for (int i = 1; i <= n; i++) M *= m[i];
for (int i = 1; i <= n; i++) {
ll w = M / m[i];
gcd(m[i], w, d, d, y);
x = (x + y * w * a[i]) % M;
}
return (x + M) % M;
}
void dfs(int dep) { //枚举y的所有可能组合
if (dep == c + 1) sol.push_back(china(c, a, x));
else {
for (int i = 1; i <= k[dep]; i++) {
a[dep] = y[dep][i];
dfs(dep + 1);
}
}
}
void solveChina() {
sol.clear();
dfs(1);
sort(sol.begin(), sol.end());
ll M = 1;
for (int i = 1; i <= c; i++) M *= x[i]; //M为lcm
for (int i = 0; s; i++) {
for (int j = 0; j < sol.size(); j++) {
ll n = M * i + sol[j];
if (n > 0) {
printf("%lld\n", n);
if (--s == 0) break;
}
}
}
}
void solve(int bestc) {
for (int i = 1; i <= c; i++) {
if (i == bestc) continue;
vis[i].clear(); //set数组只能挨个清空
for (int j = 1; j <= k[i]; j++) vis[i].insert(y[i][j]);
}
for (int i = 0; s; i++) { //从小到大枚举
for (int j = 1; j <= k[bestc]; j++) {
ll n = 1ll * x[bestc] * i + y[bestc][j];
if (n == 0) continue;
int flag = 1;
for (int k = 1; k <= c; k++) { //判断其他组的条件是否满足
if (k == bestc) continue;
if (vis[k].find(n % x[k]) == vis[k].end()) {
flag = 0;
break;
}
}
if (flag) {
printf("%lld\n", n);
if (--s == 0) break;
}
}
}
}
int main() {
while (~scanf("%d%d", &c, &s) && c) {
bestc = tot = 1;
for (int i = 1; i <= c; i++) {
scanf("%d%d", &x[i], &k[i]);
tot *= k[i];
for (int j = 1; j <= k[i]; j++) {
scanf("%d", &y[i][j]);
}
sort(y[i] + 1, y[i] + k[i] + 1); //便于枚举和二分查找
if (k[i] * x[bestc] < k[bestc] * x[i]) bestc = i;
}
if (tot > 1e4) solve(bestc);
else solveChina();
printf("\n");
}
return 0;
}