UVA 11754

这道题的题面意思就是

给出C,S,C代表着条件个个数,S代表你要输出答案的个数(因为肯定有多个答案);

然后下面有C行,每行有先是X,k;然后后面紧跟着k个数字。满足这个条件的情况就是N%X=k1,k2...ki;

要你求出最小的N满足这C个条件;

看题面,就知道肯定有关中国剩余定理。

有两种方法:

第一,强行暴利枚举,也就是从全部条件的k中,找出所有的组合,用扩展中国剩余定理就能得到答案;

第二,枚举N,就是选择一个条件,用这个条件中的k1,k2...kn,去枚举N,然后判断满不满足剩下的C-1个条件。

问题在于,怎么选取这个条件呢?我们的目的是降低时间复杂度,所以选取k/x最小的就可以了,至于为什么,打打草稿就能得出结论

下面附上代码

#include
using namespace std;
#define ll long long int
#define fin(a,n) for(int i=a;i<=n;i++)
#define fjn(a,n) for(int j=a;j<=n;j++)
#define fni(n,a) for(int i=n;i>=1;i--)
#define fnj(n,a) for(int j=n;j>=1;j--)
const int maxn=1e2+10;
const int inf=1e5+10;
ll C,S;
int Y[maxn][maxn],X[maxn],k[maxn];
vectorans;
int a[maxn];
setval[maxn];
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)//扩展欧几里得求 
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x*(a/b);
    }
}

ll china(int n,int *a,int *m)//扩展中国剩余定理 
{
	ll res=0,d,y,M;
	M=1;
	fin(1,n)
	M*=m[i];
	fin(1,n)
	{   ll w=M/m[i];
		exgcd(m[i],w,d,d,y);
		res=(res+a[i]*w*y)%M;
	}
	return (res%M+M)%M;
}
void dfs(int dep)//dfs求组合数 
{   
	if(dep==C+1)
	ans.push_back(china(C,a,X));
    for(int i=1;i<=k[dep];i++)
    {
    	a[dep]=Y[dep][i];
    	dfs(dep+1);
	}
}
void solve(int S)//正常方法,dfs枚举答案 
{   ans.clear();//清空答案组 
	dfs(1);//从第一组开始枚举 
	sort(ans.begin(),ans.end());
	ll M=1;
	fin(1,C)
	{
		M*=X[i];
	}
		for(int i=0;S!=0;i++)
		{
		   for(int j=0;j0)//这里判断很必要,因为有可能这个数是0
		      { 
		   	   printf("%lld\n",ans[j]+M*i);
		   	   S--;
		   	   if(S==0)break;
		      }
		   }
		}
}
void solve_max(int S,int in)
{
	fin(1,C)
	{   val[i].clear();//清空当前组 
		if(i!=in)//如果不是出发组 
		{   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[in];i++)
		{
			ll ans=X[in]*t+Y[in][i];//枚举答案 
			if(ans==0)
			continue;
			bool ok=true;//先判断这个数符合 
			for(int j=1;j<=C;j++)//从第一组开始判断 
			{
				if(j!=in)//不是入口组 
				{
					if(!val[j].count(ans%X[j]))//如果当前组不符合 
					{
						ok=false;//修改bool 
						break;
					}
				}
			}
			if(ok)
		{
			printf("%lld\n",ans);
			S--;
			if(S==0)break;
		}
		}
		
	}
	
}
int main()
{
	while(scanf("%d %d",&C,&S)==2&&C)
	{   int in=1;//in表示入口 
	    ll tot=1;
		//if(C==0&&S==0)return 0;
		fin(1,C)
		{
			scanf("%d %d",&X[i],&k[i]);
			tot*=k[i];
			fjn(1,k[i])
			{
				scanf("%d",&Y[i][j]);
			}
			sort(Y[i]+1,Y[i]+1+k[i]);
			if(k[in]*X[i]>k[i]*X[in])
			in=i;
		}
		if(tot>inf)
		solve_max(S,in);
		else
		solve(S);
		printf("\n");
	}
}

 

 

你可能感兴趣的:(数论初级)