形如a*x+b*y=c
为不定方程,a,b>0其实无所谓,因为gcd(a,b)=gcd(|a|,|b|) //gcd为最大公约数
由数论的定理所知,当c%gcd==0,不定方程有解,现在我们来求这个解.
gcd=gcd(a,b);a*b=gcd*lcm; //lcm为最小公倍数
a'=a/gcd;b'=b/gcd;c'=c/gcd; a'目前与b'互质
令c''=1=a'*x+b'*y;
利用extend_gcd求出一组特解。(x0,y0)
(x0*c'.y0*c')为 a'*x+b'*y=c'的一组特解
由方程解的思想求出其通解;x=x0*c'+b'*t; y=y0*c'-a'*t;再用通解去求解题目
今天花了一晚上时间纠结了一个显而易见的问题。果然当局者迷。
我现在就想大笑一场。哈哈哈哈哈哈哈哈
题目意思:t个case,n个野人,C,P,L分别代表初始洞穴,隔天移动洞穴数,存活天数。
问:最小的洞穴数,使得在n个野人在各自存活期不遇见。
数据:1*10^6 可以接受,所以此题以初值为野人中初始洞穴最大数,枚举就可以过了。
源代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 5 using namespace std; 6 7 int x,y,gcd,n; 8 int C[20],P[20],L[20]; 9 10 void extend_gcd(int a,int b) 11 { 12 if(b==0) 13 { 14 x=1; 15 y=0; 16 gcd=a; 17 return; 18 } 19 extend_gcd(b,a%b); 20 int tmp=x; 21 x=y; 22 y=tmp-a/b*y; 23 return; 24 } 25 26 bool solve(int i,int j,int m) 27 { 28 int c=C[i]-C[j]; 29 int b=m; 30 int a=P[j]-P[i]; 31 extend_gcd(a,b); 32 if(c%gcd) return false; 33 x*=c/gcd; 34 if(gcd<0) gcd=-gcd; 35 //因为x=x+b/gcd*k,所以x中包含了若干个b/gcd的代数和差 36 //因此用x对b/gcd取模,注意负数得x的最小正值 37 x=(x%(b/gcd)+b/gcd)%(b/gcd); 38 if(x<=L[i] && x<=L[j]) 39 return true; 40 return false; 41 } 42 bool test(int m) 43 { 44 for(int i=0;i<n-1;i++) 45 for(int j=i+1;j<n;j++) 46 { 47 if(solve(i,j,m)) return false; 48 } 49 return true; 50 } 51 52 53 int main() 54 { 55 int t; 56 scanf("%d",&t); 57 while(t--) 58 { 59 scanf("%d",&n); 60 int lb=0; 61 for(int i=0;i<n;i++) 62 { 63 scanf("%d %d %d",C+i,P+i,L+i); 64 lb=max(lb,C[i]); 65 C[i]--; 66 } 67 while(lb<=1000000) 68 { 69 if(test(lb)) break; 70 lb++; 71 } 72 printf("%d ",lb); 73 } 74 return 0; 75 } 76 //提供测试数据 77 Sample Input 78 2 79 3 80 1 3 4 81 2 7 3 82 3 2 1 83 5 84 1 2 14 85 4 4 6 86 8 5 9 87 11 8 13 88 16 9 10 89 Sample Output 90 6 91 33
补充膜逆元//
a≡a^-1(mod n) 表示a同余n于a^-1 变式子:a*a^-1≡1(mod n) 即a^-1为a的膜逆元 有解的充分必要条件为a与n互质
若膜逆元存在,可以转换为a*b≡1(mod n)利用extend_gcd求不定方程的解来得到乘法逆元的通解。
a*b+n*y=1 extend_gcd(a,n) 解出一个b(mod n)取其最小值(正负特别考虑)
一般的:a*b+n*y=g a与n互质,g!=0则无解
因为:(a*b+n*y)(mod n)=g(mod n) a*b(mod n)=g(mod n)(g<n) g mod n !=1
则a*b!=1即b不是a的模逆元
欧拉定理中的模逆元
a与n为两个互质的正整数,a^f(n)≡1(mod n) f(n)为小于n且与n互质的正整数个数
a^f(n)=a*a^(f(n)-1)≡1(mod n)即a^(f(n)-1)为a的模n之逆元
中国剩余定理
x≡a1(mod m1)
x≡a2(mod m2)
...
x≡ai(mod mi)
m1,m2....mi互质,则对任意的a1,a2...ai方程组有解
且通解为:x=a1*t1*M1+a2*t2*M2+....+ai*ti*Mi+k*M (Mi=M/mi, M=m1*m2*...*mi)
具体证明及例子参考wiki
大致题意:
给你两个元素C,S C为行数,也代表互质元的个数,S代表要输入的最小的S个解。
接着的X,k为质数元,k代表要求的n对X取余之后的存在的余数个数,(这里n不唯一,所以对X有多个余数,
申明:一个数n对另一个数m取余数有且仅有一个)
具体想法:由于k为(1,100)当有C=9个k的时候dfs去搜的话会爆掉。
所以用特殊枚举法,当total=k[0]*k[1]*....*k[i]<10000的时候用dfs去搜
否则 找到一个基元s 用t*X[s]+Y[s][j]去枚举。 //这里s为当k/x最小的时候所取的值
//因为k越小,x越大,代表余数个数越小,除数越大,所以枚举所需要的次数越少。
//详细解释-重点: n=t*X[s]+Y[s][j] (t=0,1.....,)(j=0,1....k[s])因为n%X[s]=Y[s][j]。
//即无论要求多少个n,都能够从一个X[s]和其余数得出来。所以选择需要枚举次数最小的那一组(X[s],Y[s][j])(j=0,1...k[s])速度最快。
源代码://写了一下午,终于干掉了,进步了不少,继续加油!
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <set> 6 #include <vector> 7 8 using namespace std; 9 10 typedef long long ll; 11 const int maxc=15; 12 const int maxs=105; 13 const int limit=10000; 14 15 ll total; 16 int C,S,bestc; 17 int X[maxc],k[maxc],Y[maxc][maxs]; 18 19 void init() 20 { 21 total=1; 22 bestc=0; 23 for(int i=0;i<C;i++) 24 { 25 scanf("%d %d",&X[i],&k[i]); 26 total*=k[i]; 27 for(int j=0;j<k[i];j++) 28 scanf("%d",&Y[i][j]); 29 sort(Y[i],Y[i]+k[i]); 30 if(X[bestc]*k[i]<X[i]*k[bestc]) 31 bestc=i; 32 } 33 } 34 35 set<int> vis[maxc]; 36 void solveEnum(int s) 37 { 38 for(int i=0;i<C;i++) 39 { 40 if(i==s) 41 continue; 42 vis[i].clear(); 43 for(int j=0;j<k[i];j++) 44 vis[i].insert(Y[i][j]); 45 } 46 for(int t=0;S;t++) 47 { 48 for(int i=0;i<k[s];i++) 49 { 50 ll n=(ll)X[s]*t+Y[s][i]; 51 if(n==0) 52 continue; 53 bool flag=true; 54 for(int j=0;j<C;j++) 55 { 56 if(j==s) 57 continue; 58 if(!vis[j].count(n%X[j])) 59 { 60 flag=false; 61 break; 62 } 63 } 64 if(flag) 65 { 66 printf("%lld ",n); 67 if(--S==0) 68 break; 69 } 70 } 71 } 72 } 73 74 void extend_gcd(ll a,ll b,ll& d,ll& x,ll& y) 75 { 76 if(!b) 77 { 78 x=1; 79 y=0; 80 d=a; 81 return; 82 } 83 extend_gcd(b,a%b,d,y,x); 84 y-=a/b*x; 85 return; 86 } 87 88 vector<int> sol; 89 int a[maxc]; 90 int China(int n,int* s,int* m) 91 { 92 ll M=1,d,y,x=0; 93 for(int i=0;i<n;i++) 94 M*=m[i]; 95 for(int j=0;j<n;j++) 96 { 97 ll w=M/m[j]; 98 extend_gcd(m[j],w,d,d,y); 99 x=(x+y*w*a[j])%M; 100 } 101 return (x+M)%M; 102 } 103 104 void dfs(int dep) 105 { 106 if(dep==C) 107 sol.push_back(China(C,a,X)); 108 else 109 { 110 for(int i=0;i<k[dep];i++) 111 { 112 a[dep]=Y[dep][i]; 113 dfs(dep+1); 114 } 115 } 116 } 117 118 void solveChina() 119 { 120 sol.clear(); 121 dfs(0); 122 sort(sol.begin(),sol.end()); 123 ll M=1; 124 for(int i=0;i<C;i++) 125 M*=X[i]; 126 for(int t=0;S;t++) 127 { 128 for(int j=0;j<sol.size();j++) 129 { 130 ll n = M*t+sol[j]; 131 if(n>0) 132 { printf("%lld ",n); 133 if(--S==0) 134 break; 135 } 136 } 137 } 138 } 139 int main() 140 { 141 while(scanf("%d %d",&C,&S) == 2 && C+S) 142 { 143 init(); 144 if(total>limit) 145 solveEnum(bestc); 146 else 147 solveChina(); 148 puts(" "); 149 } 150 return 0; 151 }