中国剩余定理:
类似于韩信点兵的问题。
求出方程 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[n] )中只有唯一解,取模即可
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 "); else printf("%I64d ",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 "); else printf("%I64d ",num); } return 0; }