extend_gcd求解不定方程/膜线性方程/乘法(模)逆元

形如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;再用通解去求解题目

今天花了一晚上时间纠结了一个显而易见的问题。果然当局者迷。

我现在就想大笑一场。哈哈哈哈哈哈哈哈

UVA,10413

题目意思: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

uva 11754

 

大致题意:

给你两个元素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 }
活在现实,做在梦里。
原文地址:https://www.cnblogs.com/do-it-best/p/5335172.html