扩展欧几里得&&中国剩余定理 解线性同余方程组 专题

来源:互联网 发布:php短信发送接口 代码 编辑:程序博客网 时间:2024/06/06 01:44

poj1061  扩展欧几里得解方程

#include<iostream>#include<cstdlib>#include<stdio.h>using namespace std;__int64 exGcd(__int64 a,__int64 b, __int64 &xx, __int64 &yy);int main(){       __int64 x,y,m,n,L,xx,yy;       scanf("%I64d%I64d%I64d%I64d%I64d",&x,&y,&m,&n,&L);       __int64 d=exGcd(n-m,L,xx,yy);//a,n,x,y  ax+ny=b;(n-m)*xx+l*yy=x-y;       if((x-y)%d!=0)              cout<<"Impossible"<<endl;       else       {              xx=xx*((x-y)/d);              int s=L/d;              xx=(xx%s+s)%s;              printf("%I64d\n",xx);             /* xx=(x-y)/d*xx%L+L;              printf("%I64d\n",xx%(L/d));*/              /*e = b / d * x % n + n;              return e % (n / d);*/       }       system("PAUSE");       return 0;}__int64 exGcd(__int64 a,__int64 b, __int64 &xx, __int64 &yy){       if(b == 0)       {       xx = 1;       yy = 0;       return a;       }       __int64 r = exGcd(b, a % b, xx, yy);       __int64 t = xx;       xx = yy;       yy = t - a / b * yy;    return r;}

Poj2115

C Looooops

Time Limit: 1000MS  Memory Limit: 65536K

Total Submissions: 11401  Accepted: 2700

Description

A Compiler Mystery: We are given a C-language style for loop of type

for (variable = A; variable != B; variable += C)

  statement;

I.e., a loop which starts by setting variable to value A and while variable is not equal to B, repeats statement followed by increasing the variable by C. We want to know how many times does the statement get executed for particular values of A, B and C, assuming that all arithmetics is calculated in a k-bit unsigned integer type (with values 0 <= x < 2k) modulo 2k.

Input

The input consists of several instances. Each instance is described by a single line with four integers A, B, C, k separated by a single space. The integer k (1 <= k <= 32) is the number of bits of the control variable of the loop and A, B, C (0 <= A, B, C < 2k) are the parameters of the loop.

The input is finished by a line containing four zeros.

Output

The output consists of several lines corresponding to the instances on the input. The i-th line contains either the number of executions of the statement in the i-th instance (a single integer number) or the word FOREVER if the loop does not terminate.

Sample Input

3 3 2 16

3 7 2 16

7 3 2 16

3 4 2 16

0 0 0 0

Sample Output

0

2

32766

FOREVER

这题与上题大同小异。

根据题意,我们有A+CX=B(mod M)  M=1<<k

cx+2^ky=b-a#include<iostream>#include<cstdlib>#include<stdio.h>#include<math.h>using namespace std;__int64 exGcd(__int64 a,__int64 b, __int64 &xx, __int64 &yy);int main(){       __int64 xx,yy,a,b,c,k;       while(scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&k)!=EOF)       {        if(a==0&&b==0&&c==0&&k==0)        break;        k=pow(2,k+0.0);        __int64 d=exGcd(c,k,xx,yy);//a,n,x,y  ax+ny=b;(n-m)*xx+l*yy=x-y;       if((b-a)%d!=0)              cout<<"FOREVER"<<endl;       else       {              /*xx=xx*((x-y)/d);              int s=L/d;              xx=(xx%s+s)%s;              printf("%I64d\n",xx);*/              xx=(b-a)/d*xx%k+k;              printf("%I64d\n",xx%(k/d));              /*e = b / d * x % n + n;              return e % (n / d);*/       }       }      // system("PAUSE");       return 0;}__int64 exGcd(__int64 a,__int64 b, __int64 &xx, __int64 &yy){       if(b == 0)       {       xx = 1;       yy = 0;       return a;       }       __int64 r = exGcd(b, a % b, xx, yy);       __int64 t = xx;       xx = yy;       yy = t - a / b * yy;    return r;}

Poj2142

The Balance

Time Limit:5000MS

Memory Limit:65536K

Total Submissions:2337

Accepted:1012

Description

Ms. Iyo Kiffa-Australis has a balance and only two kinds of weights to measure a dose of medicine. For example, to measure 200mg of aspirin using 300mg weights and 700mg weights, she can put one 700mg weight on the side of the medicine and three 300mg weights on the opposite side (Figure 1). Although she could put four 300mg weights on the medicine side and two 700mg weights on the other (Figure 2), she would not choose this solution because it is less convenient to use more weights.
You are asked to help her by calculating how many weights are required.


Input

The input is a sequence of datasets. A dataset is a line containing three positive integers a, b, and d separated by a space. The following relations hold: a != b, a <= 10000, b <= 10000, and d <= 50000. You may assume that it is possible to measure d mg using a combination of a mg and b mg weights. In other words, you need not consider "no solution" cases.
The end of the input is indicated by a line containing three zeros separated by a space. It is not a dataset.

Output

The output should be composed of lines, each corresponding to an input dataset (a, b, d). An output line should contain two nonnegative integers x and y separated by a space. They should satisfy the following three conditions.

· You can measure dmg using x many amg weights and y many bmg weights.

· The total number of weights (x + y) is the smallest among those pairs of nonnegative integers satisfying the previous condition.

· The total mass of weights (ax + by) is the smallest among those pairs of nonnegative integers satisfying the previous two conditions.


No extra characters (e.g. extra spaces) should appear in the output.

Sample Input

700 300 200

500 200 300

500 200 500

275 110 330

275 110 385

648 375 4002

3 1 10000

0 0 0

Sample Output

1 3

1 1

1 0

0 3

1 1

49 74

3333 1

这题跟上面的比起来稍微有点麻烦。

大概意思 给定 a b k找到满足ax+by=k的令|x|+|y|最小(等时令a|x|+b|y|最小)不妨ab

解两次方程比较大小 一个方程ax+by=k,另一个是bx+ay=k

#include<iostream>#include<stdio.h>#include<stdlib.h>#include<math.h>using namespace std;__int64 xx, yy;__int64 exGcd(__int64 a,__int64 b){       if(b == 0)       {       xx = 1;       yy = 0;       return a;       }       __int64 r = exGcd(b, a % b);       __int64 t = xx;       xx = yy;       yy = t - a / b * yy;    return r;}int main(){       __int64 x,y,X,Y,a,b,di,maxn1,maxn2;       while(scanf("%I64d%I64d%I64d",&a,&b,&di)!=EOF)       {        if(a==0&&b==0&&di==0)        break;        __int64 d=exGcd(a,b);        X=di/d*xx%b+b;        X%=(b/d);        Y=abs(di-a*X)/b;        Y=abs(Y);        maxn1=X+Y;        maxn2=a*X+b*Y;        d=exGcd(b,a);        x=di/d*xx%a+a;        x%=(a/d);        y=abs(di-b*x)/a;        y=abs(y);        if(maxn1>x+y)        {            X=y;            Y=x;        }        else if((maxn1==x+y)&&(maxn2>b*x+a*y))        {            X=y;            Y=x;        }        cout<<X<<" "<<Y<<endl;       }      // system("PAUSE");       return 0;}


poj1006  解线性同余方程

方程组为:

x= p(mod  23)

x=e(mod 28)

x=i(mod 33)

由于已知了23,28,33我们可以手算,将逆求出来,利用中国剩余定理的解法就能解出来

#include<iostream>using namespace std;int main(){    int a,b,c,d;    int n(0);    while(cin>>a>>b>>c>>d)    {        n++;        if(a==-1&&b==-1&&c==-1&&d==-1)break;        int i;        i=(5544*a+14421*b+1288*c-d)%(23*28*33);        if(i<=0)cout<<"Case "<<n<<": the next triple peak occurs in "<<21252-d<<" days.\n";        else cout<<"Case "<<n<<": the next triple peak occurs in "<<i<<" days.\n";    }    return 0;}

我们也可以用扩展欧几里得来求逆,或者直接暴力,下面是直接暴力的代码

#include<iostream>#include<cstdlib>#include<stdio.h>#include<cmath>using namespace std;int p,e,I,d;int m[4],M,f[4],g[4],b[4];int count;int main(){    m[0]=23,m[1]=28,m[2]=33;    count=1;    while(scanf("%d%d%d%d",&p,&e,&I,&d)!=EOF)    {        if(p==-1&&e==-1&&I==-1&&d==-1)        break;        b[0]=p,b[1]=e,b[2]=I;        int M=1;        for(int i=0;i<3;i++)        M=M*m[i];        for(int i=0;i<3;i++)        f[i]=M/m[i];        for(int i=0;i<3;i++)         {             int j=1;             while((f[i]*j)%m[i]!=1)//用循环计算y[i]应该是整个程序中最烂的算法                 j++;             g[i]=j;         }        /*for(int i=0;i<3;i++)        cout<<g[i]<<endl;*/        int x;        x=0;        for(int i=0;i<3;i++)        x+=g[i]*f[i]*b[i];        x=x%M;        x=x-d;        if(x<=0)        x+=21252;        printf("Case %d: the next triple peak occurs in %d days.\n",count++,x);    }}


fzu 1402Problem 1402 猪的安家

Accept: 647 Submit: 4558
Time Limit: 1000 mSec Memory Limit : 32768 KB

Problem Description

AndyMary养了很多猪。他们想要给猪安家。但是Andy没有足够的猪圈,很多猪只能够在一个猪圈安家。举个例子,假如有16头猪,Andy建了3个猪圈,为了保证公平,剩下1头猪就没有地方安家了。Mary生气了,骂Andy没有脑子,并让他重新建立猪圈。这回Andy建造了5个猪圈,但是仍然有1头猪没有地方去,然后Andy又建造了7个猪圈,但是还有2头没有地方去。Andy都快疯了。你对这个事情感兴趣起来,你想通过Andy建造猪圈的过程,知道Andy家至少养了多少头猪。

Input

输入包含多组测试数据。每组数据第一行包含一个整数n (n <= 10)Andy建立猪圈的次数,解下来n行,每行两个整数ai, bi( bi <= ai <= 1000),表示Andy建立了ai个猪圈,有bi头猪没有去处。你可以假定(ai, aj) = 1.

Output

输出包含一个正整数,即为Andy家至少养猪的数目。

Sample Input

3

3 1

5 1

7 2

Sample Output

16

典型的利用中国剩余定理求解线性方程组的题。

根据题意我们设有x只猪,则有方程组:

x=b1 mod a1

x=b2 mod a2

x=b3 mod a3

……

x=bi mod ai

题中说了ai是两两互质的,所以我们可以直接利用中国剩余定理解题。

那么题中的解为x=M1'M1b1+M2'M2b2……+Mk'MKbk(mod m)   m=a1*a2*a3……ai

Mi'Mi= 1(mod mi)    即Mi'Mi+mi*y=1求逆用扩展欧几里得解出Mi'

#include <iostream>#include <cstdio>#include <cstring>using namespace std;typedef __int64 int64;int64 a[15],b[15];int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y){    if(b==0)    {        x=1,y=0;        return a;    }    int64 d = Extend_Euclid(b,a%b,x,y);    int64 t = x;    x = y;    y = t - a/b*y;    return d;}//求解模线性方程组x=ai(mod ni)int64 China_Reminder(int len, int64* a, int64* n){    int i;    int64 N = 1;    int64 result = 0;    for(i = 0; i < len; i++)        N = N*n[i];    for(i = 0; i < len; i++)    {        int64 m = N/n[i];        int64 x,y;        Extend_Euclid(m,n[i],x,y);        x = (x%n[i]+n[i])%n[i];        result = (result + m*a[i]*x%N)%N;    }    return result;}int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        for(int i = 0; i < n; i++)            scanf("%I64d %I64d",&a[i],&b[i]);        printf("%I64d\n",China_Reminder(n,b,a));    }    return 0;}

Poj2891解线性同余方程,判有没有解

除数 余数

2

8 7

11 9

输出

31

方程组

x= a1(mod b1)

x=a2 (mod b2)

……

x=ai(mod bi)

不能利用中国剩余定理解,因为b1,b2……bi不一定两两互素

#include <cstdlib>#include <iostream>#include <stdio.h>using namespace std;long long extgcd(long long a,long long b,long long &x,long long &y){if (b == 0) { x=1; y=0; return a; }long long d = extgcd(b, a % b, x, y);long long t = x; x = y; y = t - a / b * y;return d;}int main(){   long long n,k,m,a,r;   bool flag=1;   while(scanf("%lld",&n)!=-1)   {      flag=1;      if(n==0)      {printf("-1\n");continue;}      scanf("%lld%lld",&k,&m);      while(--n)      {        scanf("%lld %lld",&a,&r);        if(flag)        {        //  printf("%I64d %I64d  ",k,m);          long long x,y;          long long d=extgcd(k,a,x,y);          if((r-m)%d) {flag=0;continue;}          x=(x*(r-m)/d+a/d)%(a/d);          long long side=k/d*a;          m=((x*k+m)%side)%side;          if(m<0) m+=side;          k=side;/*kx+m=Max+r=M联立得:kx-ax=r-m更新k为LCM(k,a)------>side=k/d*a;d为gcd(k,a);m为求得的M*/        }      }      if(!flag) printf("-1\n");      else      printf("%lld\n",m);   }}


hdu1573

X问题

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1926    Accepted Submission(s): 573


Problem Description
求在小于等于N的正整数中有多少个X满足:X mod a[0] = b[0], X mod a[1] = b[1], X mod a[2] = b[2], …, X mod a[i] = b[i], … (0 < a[i] <= 10)。


 

Input
输入数据的第一行为一个正整数T,表示有T组测试数据。每组测试数据的第一行为两个正整数N,M (0 < N <= 1000,000,000 , 0 < M <= 10),表示X小于等于N,数组a和b中各有M个元素。接下来两行,每行各有M个正整数,分别为a和b中的元素。


 

Output
对应每一组输入,在独立一行中输出一个正整数,表示满足条件的X的个数。


 

Sample Input
310 31 2 30 1 2100 73 4 5 6 7 8 91 2 3 4 5 6 710000 101 2 3 4 5 6 7 8 9 100 1 2 3 4 5 6 7 8 9


 

Sample Output
103


 

Author
lwg


 

Source
HDU 2007-1 Programming Contest


 

Recommend
linle
x=a1(mod b1)
x=a2(mod b2)
……
x=ai(mod bi)
判断同余方程在某个范围内解的个数,在上题的基础上解一个不等式,x+k*y<=N  若x不等于0,那么解得的x即为要求解的个数,否则x-1为所求。x为同余方程组的解,k为b1,b2……bi的最小公倍数,y为要求的解得个数,N是题目中所给的范围。
#include <cstdlib>#include <iostream>#include <stdio.h>using namespace std;long long extgcd(long long a,long long b,long long &x,long long &y){if (b == 0) { x=1; y=0; return a; }long long d = extgcd(b, a % b, x, y);long long t = x; x = y; y = t - a / b * y;return d;}int main(){   long long n,t,mi,a[22],r[22];   bool flag;   scanf("%I64d",&t);   while(t--)   {      scanf("%I64d%I64d",&n,&mi);      flag=true;      for(long long  i=0;i<mi;i++)      scanf("%I64d",&a[i]);      for(long long  i=0;i<mi;i++)      scanf("%I64d",&r[i]);      long long  m,k;      k=a[0];m=r[0];      long long side;      for(long long  i=1;i<mi;i++)      {        if(flag)        {          long long x,y;          long long d=extgcd(k,a[i],x,y);          if((r[i]-m)%d) {flag=false;break;}          x=(x*(r[i]-m)/d+a[i]/d)%(a[i]/d);          // x*=(r[i]-m)/d;          // m=m+k*x;          side=k/d*a[i];          m=((x*k+m)%side)%side;          while(m<0) m+=side;          m%=side;          k=side;/*kx+m=Max+r=M联立得:kx-ax=r-m更新k为LCM(k,a)------>side=k/d*a;d为gcd(k,a);m为求得的M*/        }      }      while(m<0) m+=k;      m%=k;      if(!flag||m>n) puts("0");      else      {          long long res=(n-m)/k+1;          if(m==0) res--;          printf("%lld\n",res);      }  }}