您的位置:首页 > 其它

中国剩余定理

2015-12-30 21:19 471 查看
中国剩余定理:

类似于韩信点兵的问题。

求出方程 x = a[i](mod m[i]),mi互为素数

令Mi = m1*m2...*m[i-1]*m[i+1]*....

那么 gcd(Mi,mi) = 1. 故存在pi,qi使 Mi*pi + mi*qi = 1(扩展欧几里得)

令ei = Mi*pi

那么有 ei = 0(mod mj),j != i

ei = 1(mod mj),j = i

所以:

e0*a0 + e1*a1 + ..... + en*an是方程的解,

在[ 0,m[0]*.....*m
)中只有唯一解,取模即可

poj 1006 模板题

大意:

人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这天,人在对应的方面表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。

ll CRT(ll a[],ll m[],ll n)
{
ll M = 1;
for(ll i = 0; i < n; i++) M *= m[i];

ll ret = 0;

for(ll i = 0; i < n; i++)
{
ll x,y;
ll tm = M/m[i];
ex_gcd(tm,m[i],x,y);
ret = (ret+tm*x*a[i])%M;
}
return (ret+M)%M;
}


poj 2891

求解mi不互素的问题,可以模仿中国剩余定理,两两合并

对于 x mod a0 = r0,可以知道最小值为 a0 + r0

若同时存在x mod a1 = r1 => (k0*a0 + r0)mod a1 = r1,

那么 k1*a1 + r1 = k0*a0 + r0

于是得到 k0*a0 - k1*a1 = r1 - r0,利用扩展欧几里得可以求出x,公约数t

(但是前提是 a*m + b*n = gcd(a,b) ), 所以(r1 - r0)%t必需为0,否则不满足公式

于是我们可以得出k0 = x*(r1-r0)/t , x实际来自( a0*x + a1*y == gcd(a0,a1) )

所以这个公共的x1 = r0 + k0*x. 于是我们得到一个新的 a = x1 , r = x1 % lcm(a0,a1)

于是我们可以往后面慢慢推了。

假设有3对数: 8 4 : 7 3 : 12 7

x = 8*x0 + 4 = 7*x1 + 3 可以求出x为 11,于是把11作为r

a*t + r = 12*t1 + 7, 于是a要满足能同时除尽7和8,即它们的最小公倍数 /*个人见解

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps=1e-10;
const int inf = 0x3f3f3f;
const int maxn = 100005;

ll ans,d;
int flag;

ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
ll ret;
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ret = ex_gcd(b,a%b,x,y);
ll tmp =x;
x = y;
y = tmp-(a/b)*y;
return ret;
}

ll get(ll m1,ll m2,ll a)
{
ll x,y;
d = ex_gcd(m1,m2,x,y);
if(a % d != 0)                            //无法满足等式
{
flag = 0;
}

ll t = (x*(a/d)%m2+m2)%m2;               //得出要求的x1
return t;
}

int main()
{
ll m1,m2,a1,a2;
ll k;
while(scanf("%I64d",&k) != EOF)
{
flag = 1;
scanf("%I64d%I64d",&m1,&a1);
ans = a1;
for(ll i = 1; i < k; i++)
{
scanf("%I64d%I64d",&m2,&a2);
ll t = get(m1,m2,a2-a1);
ans += (m1*t);
m1 = m1*m2/d;                          //获得最小公倍数
ans = (ans%m1+m1)%m1;
a1 = ans;
}
if(!flag)
printf("-1\n");
else
printf("%I64d\n",ans);
}
return 0;
}


  

hdu 1573

题意:

给你两组数a[],b[].让你求出≤n的正整数中,有多少个满足 x % a[i] = b[i]

感觉和上面那个差不多吧,合并后求出最小的值。 然后通过加所有数的最小公倍数得出结果

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps=1e-10;
const int inf = 0x3f3f3f;
const int maxn = 100005;
ll a[15];
ll b[15];
ll ans,d;
int flag;

ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
ll ret;
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ret = ex_gcd(b,a%b,x,y);
ll tmp =x;
x = y;
y = tmp-(a/b)*y;
return ret;
}

ll get(ll m1,ll m2,ll a)
{
ll x,y;
d = ex_gcd(m1,m2,x,y);
if(a % d != 0)                            //无法满足等式
{
flag = 0;
}

ll t = (x*(a/d)%m2+m2)%m2;               //得出要求的x1
return t;
}

int main()
{
ll m1,m2,a1,a2;
ll  n;
ll m;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&n,&m);
for(int i = 0; i < m; i++)
scanf("%d",&a[i]);

for(int j = 0; j < m; j++)
scanf("%d",&b[j]);

flag = 1;
ans = b[0];
m1 = a[0],a1 = b[0];
for(ll i = 1; i < m; i++)
{
m2 = a[i],a2 = b[i];
ll t = get(m1,m2,a2-a1);
ans += (m1*t);
m1 = m1*m2/d;
ans = (ans%m1+m1)%m1;
a1 = ans;
}
ll num = 0;
while(ans <= n)
{
if(ans != 0)                          //正整数
num ++;
ans += m1;
}
if(!flag)
printf("0\n");
else
printf("%I64d\n",num);
}
return 0;
}


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