一.题目链接:
UVA - 11754
二.题目大意:
第一行两个整数 C,S.
接下来 C 行,每行开始有两个整数 X[i],K[i],接下来 K[i] 个整数 Y[i][1 ... K[i]]
表示存在一个正整数 N,使得 N 满足 C 个条件:N % X[i] 为 Y[i][1 ... K[i]] 之中的一个数.
输出最小的前 S 个正整数 N.
三.分析:
很容易想到枚举 Y 的所有组合情况,然后 CRT 暴力求解.
但是当 K[i] 的乘积较大时,这样肯定会收获一发 TLE...
于是这里就有一个玄学暴力解法.
找出使得 K[i] / X[i] 最小的下标 i,然后枚举 N = Y[i][j] + X[i] * t,然后逐一 check.
下面口胡一下这样做为什么可以.
由于 K[i] 的乘积比较大,因此待选数集合较多,当我选择了一个 N 后,check 成功的可能性就较大.
这也是 i 为什么选取使得 K[i] / X[i] 最小的下标的原因,K[i] 小说明当前枚举 Y 的个数少,X[i] 大可以让 N 变大的更快.
贴一个大佬的讲解
还有一个问题就是为什么当 K[i] 乘积较小时不可以直接枚举 N
其实上面已经说过, K[i] 乘积小则待选数集合小,check 成功的可能性小.
例如这组数据
3 10
1007 1 1006
1001 1 991
1003 1 1001
经实测,需要枚举 1e7 规模次 N,无疑会 TLE.
好玄学呀~~
四.代码实现:
#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;
}