就是给你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;
}