浅谈中国剩余定理
我们先来看这样一道题目:
树王种了一棵treap,她现在决定把这棵treap改造为一棵无旋多叉triep,于是她摘下了treap的所有节点,发现如果她把节点3个3个一打包,会剩下2个节点。如果她把节点5个5个一打包,会剩下3个节点,如果把节点7个7个一打包,会剩下2个节点,求这棵treap最少有多少节点?
简化后题目就是,已知x%3=2,,x%5=3,x%7=2,求x最小值?
如何解决呢?我们设k1,k2,k3满足 k1%3=1,k1%5=0,k1%7=0,k2%5=1,k2%3=0,k2%7=0.k3%7=1.,k3%3=0,k3%5=0.
那么 k1*2+k2*3+k3*2一定是满足答案的一个解。(网上说容易意会,容易意会个鬼啊)
证明: 设x=k1*2+k2*3+k3*2。 则x%3=(k1*2+k2*3+k3*2)%3=(k1*2%3+k2*3%3+k3*2%3)%3=(2+0+0)%3=2;
其他两组解同理。
那么如何求k1,k2,k3?
我们求出LCM(3,5,7)=105,设x1=LCM(3,5,7)/3=35,x2==LCM(3,5,7)/5=21,x3==LCM(3,5,7)/7=15.
那么设k1=n1*x1,k2=n2*x2,k3=n3*x3.
k1%3=1,k2%5=1,k3%7=3,可转换为 n1*x1%3=1,n2*x2%5=1,n3*x3%7=1.
然后可以用扩展欧几里得求出n1,n2,n3.进而得到 k1,k2,k3.
最后便可得到答案,而题目的通解可表示为这个数每次都加上3,5,7的最小公倍数。
好了,现在步入正题(你没看错现在我才开始讲正经的,刚才那一堆都是铺垫)
已知 x≡a[1](mod)y[1],x≡a[2](mod)y[2],x≡a[3](mod)y[3].........x≡a[n](mod)y[n].
其中 y[1],y[2],y[3]......y[n]两两互质,求x
其算法流程如下。
1.计算LCM(y1,y2......yn)。
2.从1->n,计算f[i]=LCM(y[1[,y[2],....y[n])/y[i].
3.使用扩展欧几里得定理,对同余式b[i]*f[i]%y[i]=1,计算出b[i],进而得出k[i]=b[i]*f[i].
4.从1->n,计算x=k[1]*a[1]+k[2]*a[2]+...........k[n]*a[n].
5.对x模lcm,得到答案。
接下来给出代码:
#include <bits/stdc++.h>
#define maxn 105
using namespace std;
int ex_gcd(int a,int b,int &x,int &y)//扩展欧几里得定理,解ax+by=c。
{
if(b==0)
{
x=1;
y=0;
return a;
}
int ans=ex_gcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-(a/b)*x;
return ans;
}
int a[maxn],b[maxn],n,f[maxn];
int solve()
{
int ans=0;
int lcm=1;
for(int i=1;i<=n;i++)
{
lcm*=a[i];//两两互质,直接乘了
}
for(int i=1;i<=n;i++)
{
f[i]=lcm/a[i];
}
for(int i=1;i<=n;i++)
{
int x,y,g;
g=ex_gcd(f[i],a[i],x,y);
x=(x%a[i]+a[i])%a[i];//因为扩展欧几里得求出的解可能有很多组,且可能为负,我们这步求出了一个比a[i]小且大于0的解。
ans=(ans+x*f[i]*b[i])%lcm;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);//ans%a[i]=b[i].
}
int ans=solve();
printf("%d\n",ans);
}
接下来我们谈谈扩展中国剩余定理
今天才被扩展欧拉定理gank的我来学习扩展中国剩余定理了
已知 x≡a[1](mod)y[1],x≡a[2](mod)y[2],x≡a[3](mod)y[3].........x≡a[n](mod)y[n],求x。
现在y[1],y[2]........y[n]不互质了,一般情况的中国剩余定理不适用了,这怎么搞?
考虑 两个方程,分别为x≡a[1](mod)y[1],x≡a[2](mod)y[2].
则有x=y[1]*n1+a[1],x=y[2]*n2+a[2].
合并得 y[1]*n1+a[1]=y[2]*n2+a[2].化简: y[1]*n1-y[2]*n2=a[2]-a[1].
使用扩展欧几里得定理 解除最小正整数解 n1,设k=y[1]*n1+a[1].
那么存在通解x=k+t*lcm(y[1],y[2]).有 x≡k(mod)lcm(y[1],y[2])。
一路合并下去就ok了。
接下来给出例题 POJ2891代码
传送门:Strange Way to Express Integers
#include <cstdio>
#define maxn 100005
using namespace std;
typedef long long ll;
ll ex_gcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得定理,解ax+by=c。
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll ans=ex_gcd(b,a%b,x,y);
ll temp=x;
x=y;
y=temp-(a/b)*x;
return ans;
}
ll a[maxn],b[maxn],n,f[maxn];
ll China()
{
ll mod=a[1],reminder=b[1];//模数为mod,余数为reminder。
for(int i=2;i<=n;i++)
{
ll x,y,g,temp;
g=ex_gcd(mod,a[i],x,y);
temp=b[i]-reminder;
if(temp%g!=0)
{
return -1;
}
x=x*temp/g;
ll t=a[i]/g;
x=(x%t+t)%t;
reminder=x*mod+reminder;
mod=mod/g*a[i];
reminder%=mod;
}
reminder=(reminder%mod+mod)%mod;
return reminder;
}
int main()
{
while(~scanf("%lld",&n))
{
for(ll i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
printf("%lld\n",China());
}
}

浙公网安备 33010602011771号