简介:
有一个正整数N,满足“ta除以X的余数在集合{Y1,Y2,… , Yk}”
求符合要求的最小S个解
分析:
显然我们需要中国剩余定理来求解
但是直接枚举每个集合中Y值选哪个会很慢
因此我们有一个更耿直的方法,直接枚举N
找一个k/X最小的条件(k是集合大小,X是除数)
按照t=0 , 1 , 2 , 3 ,…的顺序枚举X*t+Y
(同样的按照从小到大的顺序枚举Y)
看看是否满足条件,这个方法就可以很快的找到解
注意
如果求出的解个数不足S个,
那么我们只需要把这些可行解加上M,2M,3M …
tip
在程序中,当集合的总大小不是特别大的时候,我使用的是朴素枚举法
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;
const int LIMIT=10000;
int C,k[10],X[10],a[10];
int Y[10][102],n,S;
ll x,y,ans[LIMIT+5];
set<int> val[10];
void exgcd(ll a,ll b)
{
if (b==0)
{
x=1;y=0;
return;
}
else
{
exgcd(b,a%b);
ll tmp=x;
x=y;
y=tmp-a/b*y;
}
}
ll china(int n,int *a,int *m)
{
ll M=1;
ll an=0;
for (int i=1;i<=n;i++) M*=m[i];
for (int i=1;i<=n;i++)
{
ll w=M/m[i];
exgcd(w,m[i]);
an+=w*x*a[i]; an%=M; //x是w%m[i]意义下的逆元
}
return an;
}
void dfs(int t)
{
if (t>n)
{
ans[++ans[0]]=china(n,a,X);
return;
}
for (int i=1;i<=k[t];i++)
{
a[t]=Y[t][i];
dfs(t+1);
}
}
void solve_china(ll M)
{
memset(ans,0,sizeof(ans));
dfs(1);
sort(ans+1,ans+1+ans[0]);
for (int i=0;S!=0;i++) //构造合法的解,ans都是在%M意义下的,所以相邻的解不会超过M
for (int j=1;j<=ans[0];j++) //因此这样构造是合法的
{
ll an=M*i+ans[j];
if (an>0) printf("%lld\n",an);
S--;
if (S==0) break;
}
}
void solve(int bis)
{
for (int i=1;i<=n;i++)
if (i!=bis)
{
val[i].clear();
for (int j=1;j<=k[i];j++)
val[i].insert(Y[i][j]);
}
for (int t=0;S!=0;t++)
for (int i=1;i<=k[bis];i++)
{
ll an=(ll)t*X[bis]+Y[bis][i];
if (an<=0) continue;
bool ff=1;
for (int j=1;j<=n;j++)
if (j!=bis)
{
if (!val[j].count(an%X[j]))
{
ff=0;
break;
}
}
if (ff) printf("%lld\n",an);
S--;
if (S==0) break;
}
}
int main()
{
while (scanf("%d%d",&n,&S)!=EOF&&n)
{
int kk=0,bis=0;
ll tot=1;
for (int i=1;i<=n;i++)
{
scanf("%d",&X[i]);
tot*=X[i];
scanf("%d",&k[i]);
kk+=k[i];
for (int j=1;j<=k[i];j++)
scanf("%d",&Y[i][j]);
sort(Y[i]+1,Y[i]+1+k[i]); //从小到大排序,方便枚举
if (k[i]*X[bis]<k[bis]*X[i]) bis=i; //k[i]/X[i] min
}
if (kk<=LIMIT) solve_china(tot);
else solve(bis);
printf("\n");
}
return 0;
}