1.タイトルリンク:
グレープ-11754
2.トピックの主なアイデア:
最初の行の2つの整数CとS。
次のC行では、各行は2つの整数X [i]、K [i]で始まり、次にK [i]整数Y [i] [1 ... K [i]]
NがC条件を満たすような正の整数Nがあることを示します。N%X [i]はY [i] [1 ... K [i]]の中の数値です。
最小の最初のS個の正の整数Nを出力します。
3.分析:
Yのすべての組み合わせを列挙してから、CRTブルートフォースソリューションを列挙することは簡単に考えられます。
しかし、K [i]の積が大きい場合、これは間違いなくTLEを生成します...
だからここに形而上学的な暴力的な解決策があります。
K [i] / X [i]を最小化する添え字iを見つけ、N = Y [i] [j] + X [i] * tを列挙して、1つずつ確認します。
これが可能である理由について話しましょう。
K [i]の積が比較的大きいため、候補番号のセットが多くなります。Nを選択すると、チェックが成功する可能性が高くなります。
これが、K [i] / X [i]を最小化する添え字を選択する理由でもあります。K[i]が小さいと、列挙Yの数が少なくなり、X [i]が大きいとNの増加が速くなります。
大きな男の説明を投稿する
もう1つの質問は、K [i]の積が小さい場合にNを直接列挙できない理由です。
実際、前述のように、K [i]の積が小さい場合、候補番号のセットは少なく、チェックが成功する可能性は低くなります。
たとえば、このデータセット
3 10
1007 1 1006
1001 1 991
1003 1 1001
実際の測定後、1e7スケール×Nを列挙する必要があります。これは間違いなくTLEになります。
だから形而上学的~~
4.コードの実装:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = (int)1e1;
const int N = (int)1e2;
int c, s;
int x[M + 5];
int k[M + 5];
int y[M + 5][N + 5];
int a[M + 5];
vector <int> vx;
set <int> st[M + 5];
void init()
{
for(int i = 1; i <= c; ++i)
{
st[i].clear();
for(int j = 1; j <= k[i]; ++j)
{
st[i].emplace(y[i][j]);
}
}
}
bool check(ll n)
{
for(int i = 1; i <= c; ++i)
{
if(!st[i].count(n % x[i]))
return 0;
}
return 1;
}
void work1(int idx)
{
init();
ll n;
for(int i = 0; s; ++i)
{
for(int j = 1; j <= k[idx]; ++j)
{
n = y[idx][j] + 1ll * x[idx] * i;
if(n == 0) continue;
if(!check(n)) continue;
printf("%lld\n", n);
if(!--s) break;
}
}
}
ll exgcd(ll a, ll b, ll& x, ll& y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll z = x;
x = y;
y = z - y * (a / b);
return d;
}
int crt()
{
ll res = 0, m = 1, M, t, y;
for(int i = 1; i <= c; ++i)
m *= x[i];
for(int i = 1; i <= c; ++i)
{
M = m / x[i];
exgcd(M, x[i], t, y);
res = (res + a[i] * M % m * t % m) % m;
}
return (res + m) % m;
}
void dfs(int u)
{
if(u == c + 1)
{
vx.push_back(crt());
return;
}
for(int i = 1; i <= k[u]; ++i)
{
a[u] = y[u][i];
dfs(u + 1);
}
}
void work2()
{
vx.clear();
dfs(1);
sort(vx.begin(), vx.end());
ll m = 1;
for(int i = 1; i <= c; ++i)
m *= x[i];
for(int i = 0; s; ++i)
{
for(auto x: vx)
{
ll n = x + m * i;
if(n == 0) continue;
printf("%lld\n", x + m * i);
if(!--s) break;
}
}
}
int main()
{
while(~scanf("%d %d", &c, &s) && (c + s))
{
int idx = 1;
ll mul_k = 1;
for(int i = 1; i <= c; ++i)
{
scanf("%d %d", &x[i], &k[i]);
mul_k *= k[i];
idx = (k[idx] * x[i] > k[i] * x[idx] ? i : idx);
for(int j = 1; j <= k[i]; ++j)
scanf("%d", &y[i][j]);
sort(y[i] + 1, y[i] + k[i] + 1);
}
if(mul_k > (int)1e4) work1(idx);
else work2();
puts("");
}
return 0;
}