一.一元线性同余方程
形如 ax=b(mod m),a,b,为整数,m为正整数,x为未知数的同余式称为一元线性同余方程。
方程可以变形为:ax+my=c (1)
而对于方程 ax+by=(a,b) (2)是一定有解的。
因此可以把方程(1)转化为方程(2)求解。
利用拓展欧几里得算法,解出方程(2)的解,利用常数项的比例关系,可以推出方程(1)的解。
方程(1)有解的充要条件为 c%gcd(a,m)==0
利用通解x=x0+k*(b/gcd),y=y0-k*(a/gcd),可以求出其他解。
代码:
ll _gcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll ans=_gcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
ll extend_gcd(ll a,ll b,ll c)
{
ll x,y;
ll gcd=_gcd(a,b,x,y);
if(c%gcd)
return -1;
x=x*c/gcd;
x%=b;
for(int i=1;i<=gcd;i++)//方程所有解
ans[i]=(x+(i-1)*(b/gcd))%b;
}
题目:
poj 1061 2115 2142
二.一元线性同余方程组
先看一篇博客
对于模线性方程组,可以进行两两合并,求出方程的最终解。
通解x=x0+k*lcm,x0为最小非负解
代码:poj 2891 (最小正整数解)
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll a1,a2,r1,r2;
int k;
ll _gcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll ans=_gcd(b,a%b,x,y);
ll tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
int main()
{
while(scanf("%d",&k)!=EOF)
{
scanf("%lld%lld",&a1,&r1);
bool flag=1;
for(int i=1;i<k;i++)
{
scanf("%lld%lld",&a2,&r2);
ll c=r2-r1;
ll x,y;
ll gcd=_gcd(a1,a2,x,y);
if(c%gcd)//无解
flag=0;
ll t=a2/gcd;
x=(x*(c/gcd)%t+t)%t;//t范围内的最小整数解
r1=a1*x+r1;//!!!!!
a1=a1*(a2/gcd);//!!!!!!
}
if(flag)
printf("%lld\n",r1);
else
printf("-1\n");
}
return 0;
}
hdu 1573
求一定范围内的解的个数
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int a[12],b[12];
ll n;
int m;
int _gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int ans=_gcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%d",&n,&m);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d",&b[i]);
int a1=a[1],a2=0,r1=b[1],r2=0,gcd=1;
bool flag=1;
for(int i=2;i<=m;i++)
{
a2=a[i];
r2=b[i];
int x,y,c=r2-r1;
gcd=_gcd(a1,a2,x,y);
if(c%gcd)
{
flag=0;
break;
}
int t=a2/gcd;
x=(x*(c/gcd)%t+t)%t;
r1=a1*x+r1;
a1=a1*(a2/gcd);
}
if(!flag)
printf("0\n");
else
{
int yy=a[1];
for(int i=2;i<=m;i++)
yy=yy*a[i]/__gcd(yy,a[i]);//printf("yy=%d\n",yy);
int cnt=0;
ll x=r1;
while(x<=n)
{
cnt++;
x+=yy;//每次加a数组的最小公倍数
}
if(r1==0)//坑点:题目要求为正整数
cnt--;
printf("%d\n",cnt);
}
}
return 0;
}
hdu3579
输出最小正整数解
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int m[10],a[10];
int _gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int ans=_gcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
int main()
{
int t,cnt=0;
scanf("%d",&t);
while(t--)
{
cnt++;
memset(m,0,sizeof(m));
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&m[i]);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int m1=m[1],m2=0,a1=a[1],a2=0;
bool flag=1;
for(int i=2;i<=n;i++)
{
m2=m[i];
a2=a[i];
int c=a2-a1;
int x,y;
int gcd=_gcd(m1,m2,x,y);
if(c%gcd)
{
flag=0;
break;
}
int t=m2/gcd;
x=(x*(c/gcd)%t+t)%t;
a1=m1*x+a1;
m1=m1*(m2/gcd);
}
if(a1==0)
{//output the least positive integer(小心坑)
int lcm=m[1];
for(int i=2;i<=n;i++)
lcm=lcm*m[i]/__gcd(lcm,m[i]);
a1+=lcm;
}
printf("Case %d: ",cnt);
if(flag)
printf("%d\n",a1);
else
printf("-1\n");
}
return 0;
}
三.多元线性同余方程组