FJUT第四次周赛E题题解

Problem Description

Morning_X今天又在摸鱼了,因为Home_W忙着给新生上课,于是他就在偷偷的摸鱼

但是常在河边走哪能不湿鞋,他一不小心就被Home_W抓到了,因为Home_W上次给LIWEI出题目被解出来以后

Home_W一直在准备新的题目,用来难住这群16级的摸鱼怪,于是Home_W立刻就说出了题目:

Home_W要求Morning_X在1秒内立刻说出一个数字,这个数字必须满足C个条件,每个条件都形如“他除以X的余数都在集合{Y1,Y2……,Yk}中”,

Home_W说Morning_X的智商太低了,于是他降低了条件所有条件中的X两两互素,现在Morning_X把任务交给了你,因为他要继续去摸鱼了

你的任务就是找的最小的S个解

Input

输入包含若干组数据

每组数据的第一行为两个整数C和S(1<=C<=9,1<=S<=10)

以下C行,每行描述一个条件,首先是整数X和k(X>=2,1<=K<=100),然后是(Y1,Y2……Yk)(0<=Y1,Y2……Yk<X)

所有的X的乘积保证在32位带符号整数的范围内。输入标志结束位C=S=0

Output

对于每一组数据都输出S个最小正整数解,并按照从小到大排序

题解:这道题目比较难处理的地方在于“除以X的余数必须在集合内”,但是题目已经告诉了我们这个集合内的元素,就比较好处理多了

比较暴力的做法就是枚举每个集合中的元素,这样子我们就可以处理样例。

1 x mod 2=1,x mod 5=0,x mod 3=1 ----->x mod 30=25;
2 
3 x mod 2=1,x mod 5=0,x mod 3=2 ----->x mod 30=5;
4 
5 x mod 2=1,x mod 5=3,x mod 3=1 ----->x mod 30 =13;
6 
7 x mod 2=1,x mod 5=3,x mod 3=2 ----->x mod 30=23;
举个例子

`

当所有的k的乘积很大时,这种方法就很慢了,此时我们就有另外一组方法了,直接枚举x。找一个k/X的最小条件(比如样例中的k=2,X=5)

看是否满足条件,按照t=0,1,2……的顺序枚举所有的tX=Yi(相同的t按照从小到大的顺序枚举Yi),看是否满足条件。

因为所有的k的乘积很大,这个方法很快就能找到解

有两个需要解决的地方。

1:如果用中国剩余定理求解,若得到的解不超过S个,需要把这些解加上M,2M,3M,……,直到解够多(M为所有解的乘积)。

2:根据题意,0不能算作解,因为它不是正整数。但不要因此把,M,2M,3M……这些解也忽略了

  1 #include<stdio.h>
  2 #include<vector>
  3 #include<set>
  4 #include<algorithm>
  5 #include<stdlib.h>
  6 using namespace std;
  7 
  8 typedef long long LL;
  9 const int maxc=9;
 10 const int maxk=100;
 11 const int LIMIT=10000;
 12 
 13 set<int >values[maxc];
 14 int C,X[maxc],k[maxc];
 15 int Y[maxc][maxk];
 16 
 17 void solve_enum(int S,int bc)
 18 {
 19     for(int c=0;c<C;c++) if(c!=bc){
 20         values[c].clear();
 21         for(int i=0;i<k[c];i++) values[c].insert(Y[c][i]);
 22         }
 23 
 24     for(int t=0;S!=0;t++){
 25         for(int i=0;i<k[bc];i++){
 26             LL n=(LL)X[bc]*t+Y[bc][i];
 27             if(n==0) continue;
 28             bool ok=true;
 29             for(int c=0;c<C;c++) if(c!=bc)
 30             if(!values[c].count(n%X[c])) {ok=false;break;}
 31             if(ok) {printf("%lld
",n);if(--S == 0) break;}
 32         }
 33     }
 34 }
 35 
 36 int a[maxc];
 37 vector<LL>sol;
 38 
 39 ///n个方程 x=a[i](mod m[i]) (0<=i<n)
 40 
 41 void gcd(LL a ,LL b,LL &d,LL &x,LL &y ){
 42     if(!b)  {d=a;x=1;y=0;}
 43     else { gcd(b,a%b,d,y,x);y-=x*(a/b); }
 44 }
 45 
 46 LL china(int n,int *a,int *m)
 47 {
 48     LL M=1,d,y,x=0;
 49     for(int i=0;i<n;i++) M*=m[i];
 50 
 51     for(int i=0;i<n;i++){
 52         LL w=M/m[i];
 53         gcd(m[i],w,d,d,y);
 54         x=(x+y*w*a[i])%M;
 55     }
 56     return (x+M)%M;
 57 }
 58 void dfs(int dep){
 59     if(dep==C)
 60         sol.push_back(china(C,a,X));
 61     else for(int i=0;i<k[dep];i++){
 62         a[dep]=Y[dep][i];
 63         dfs(dep+1);
 64     }
 65 }
 66 
 67 void solve_china(int S){
 68     sol.clear();
 69     dfs(0);
 70     sort(sol.begin(),sol.end());
 71 
 72     LL M=1;
 73     for(int i=0;i<C;i++)M*=X[i];
 74 
 75     vector<LL> ans;
 76 
 77     for(int i=0;S!=0;i++){
 78         for(int j=0;j<sol.size();j++){
 79             LL n=M*i+sol[j];
 80             if(n>0){printf("%lld
",n);if(--S==0)break;}
 81         }
 82     }
 83 
 84 }
 85 
 86 int main()
 87 {
 88     int S;
 89 //    freopen("C:\Documents and Settings\Administrator\桌面\E\moyu6.in","r",stdin);
 90 //    freopen("C:\Documents and Settings\Administrator\桌面\E\moyu6.out","w",stdout);
 91     while(scanf("%d %d",&C,&S)!=EOF){
 92         if(C==0&&S==0)
 93             break;
 94         LL tot=1;
 95         int bestc=0;
 96         for(int c=0;c<C;c++){
 97             scanf("%d %d",&X[c],&k[c]);
 98             tot*=k[c];
 99             for(int i=0;i<k[c];i++) scanf("%d",&Y[c][i]);
100 
101             sort(Y[c],Y[c]+k[c]);
102 
103             if(k[c]*X[bestc]<k[bestc]*X[c]) bestc=c;
104         }
105 
106         if(tot>LIMIT) solve_enum(S,bestc);
107         else solve_china(S);
108 
109     }
110     return 0;
111 }
112 /*
113 3 2
114 2 1 1
115 5 2 0 3
116 3 2 1 2
117 */
标程
原文地址:https://www.cnblogs.com/tijie/p/9503681.html