您的位置:首页 > 其它

算法学习之模线性同余方程组(中国剩余定理+求解同余方程组) poj1006+hdu3579

2017-08-23 13:10 323 查看

中国剩余定理

中国剩余定理原文:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

解法:中国剩余定理描述的就是一元线性同余方程组(其中m1,m2,...,mn均互质)。





是整数m1,m2, ... ,mn的乘积,并设Mi是除了mi以外的n- 1个整数的乘积。



为Mi模mi的逆元,所以有


将(S)的通解形式为


(这个式子想了半天才明白是怎么来的:依次模m1,m2,...,mn,就形成了(S)方程组了,因为有下列两个方程)







在模M意义下,(S)有唯一解


(以上来自百度百科)

poj1006

题意:人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。现在给出三个日期对应于该天是体力、感情和智力的高峰。现在给你一个日期,求从这一天起过多少天三个高峰同时出现。

题解:中国剩余定理的运用,23,28和33两两互质。

代码(作为模板,复杂度O(n)):

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<bitset>

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>

#include<iomanip>
#include<iostream>

#define debug cout<<"aaa"<<endl
#define d(a) cout<<a<<endl
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;

const int N = 3 + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;
//扩展欧几里德
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x=1;
y=0;
return a;
}
LL r=exgcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-a/b*y;
return r;
}
//中国剩余定理  r[]存放余数  a[]存放两两互质的除数
LL CRT(LL a[],LL w[],int n){
LL M=1,ans=0;
for(int i=1;i<=n;i++){
M*=w[i];
}
for(int i=1;i<=n;i++){
LL x,y,temp;
temp=M/w[i];
exgcd(w[i],temp,x,y);//求逆元
ans=(ans+a[i]*temp*y)%M;
}
return (ans%M+M)%M;
}

LL a
,r
;

int main(){
int cas=0;
LL p,e,i,d,ans;
const LL temp=21252;//23,28,33的lcm
while(scanf("%lld%lld%lld%lld",&p,&e,&i,&d)){
if(p==-1&&e==-1&&i==-1&&d==-1){
break;
}
a[1]=p,a[2]=e,a[3]=i;
r[1]=23,r[2]=28,r[3]=33;
ans=CRT(a,r,3);
while(ans<=d){
ans+=temp;
}
printf("Case %d: the next triple peak occurs in %lld days.\n",++cas,ans-d);
}
return 0;
}


求解同余方程组

中国剩余定理就是在求解同余方程组,但是当除数两两不互质的情况。就不能用CRT的结论来解决。



还是这个方程组,现在取出其中两个方程:

             x≡a1(mod m1)

             x≡a2(mod m2)

转化为:

             x+m1*x1=a1

             x+m2*x2=a2

合并有:m1*x1+m2*x2=a1-a2

可以通过扩展欧几里德解出,若(a1-a2)%gcd(m1,m2)!=0,则无解

反之解出x1,带入x+m1*x1=a1,得到一个特解x0=a1-m1*x1。

通解x=x0+k*lcm(a1,a2),即x≡x0(mod lcm(a1,a2)。

这样一来两个方程就合并成一个了,将这n个方程全部合并就可以解出x了。

hdu3579

题意:同中国剩余定理原文(这里除数两两不互质)

代码(作为模板,复杂度O(n)):

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<bitset>

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>

#include<iomanip>
#include<iostream>

#define debug cout<<"aaa"<<endl
#define d(a) cout<<a<<endl
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;

const int N = 1000000 + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;

LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x=1;
y=0;
return a;
}
LL r=exgcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-a/b*y;
return r;
}
//中国剩余定理  r[]存放余数  a[]存放两两互质的除数
/*若模数两两互质,我们可以用中国剩余定理来解。
这里我们先考虑两个方程:
x≡a1(mod m1)
x≡a2(mod m2)
我们可以写成:
x+x1m1=a1
x+x2m2=a2

合并有:m1*x1+m2*x2=a1-a2
可以通过扩展欧几里德解出,若(a1-a2)%gcd(m1,m2)!=0,则无解
反之解出x1,带入x+m1*x1=a1,得到一个特解x0=a1-m1*x1。
通解x=x0+k*lcm(a1,a2),即x≡x0(mod lcm(a1,a2)。
这样一来两个方程就合并成一个了,将这n个方程全部合并就可以解出x了。
*/
LL solve(LL a[],LL r[],int n){
LL M=a[1],R=r[1],x,y,d,temp;
for(int i=2;i<=n;i++){
d=exgcd(M,a[i],x,y);
if((R-r[i])%d!=0) return -1;//无解
temp=a[i]/d;
x=(R-r[i])/d*x;//特解x1。
x=(x%temp+temp)%temp;
R-=x*M;//更新余数。
M=M*temp;////更新模数(M是lcm(a[i],M))。
R%=M;
}
return R==0?M:(R%M+M)%M;//余数全为0,则返回lcm
}

LL a
,r
;

int main(){
int n,t,cas=0;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&r[i]);
}
printf("Case %d: %lld\n",++cas,solve(a,r,n));
}
return 0;
}


留个模板

写的可能不太清楚嘿~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: