Chinese Remainder Theorem
中国剩余定理CRT
1.定义
Th.
给出一元线性同余线性方程组
…
定理指出假设素数两两互素,对任意的整数
上述方程有解,并且可以通过如下步骤构造通解:
1>计算(即,Mi是除之外的其他整数的乘积)
2>计算为模的逆,对于计算满足的
3>上述方程的通解为:
在模的意义下,方程组只有一个解
2.写法
1.互质的写法(useless)
$x = 270+321+415 = 263 2(1,0,0) 3*(0,1,0) 4*(0,0,1) (2,0,0) (0,0,4) (2,3,4)x105 = 53$
总结:
对于个
我们先去解个
$xi \bmod (m1m2..mi-1mi+1…mn) = 0xi \bmod (\frac{M}{mi}) = 0 xi \bmod mi = 1xi = (\frac{M}{mi})*t(\frac{M}{mi})*t \bmod mi = 1$
比如上述例子
即$Miti ≡ 1(\bmod mi)xi = Miti x = Σaixi$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N = 5e5 + 7; int n, a[N], m[N]; ll exgcd(ll a, ll b, ll &x, ll &y) { if(b == 0){ x = 1; y = 0; return a; } ll d = exgcd(b, a % b, x, y); ll z = x;x = y;y = z - (a / b) * x; return d; } int main() { int t; cin>>t; while(t--) { ll M = 1; scanf("%d", &n); for(int i = 1; i <= n; ++ i) { scanf("%d%d", &a[i], &m[i]); M *= m[i]; } ll res = 0; for(int i = 1; i <= n; ++ i) { ll Mi = M / m[i]; ll ti, y; ll d = exgcd(Mi, m[i], ti, y); ti = (ti % m[i] + m[i]) % m[i]; res += a[i] * ti * Mi; } printf("%lld\n", (res % M + M) % M); } return 0; }
|
2.对于不互质的情况(增量法)
一开始有两个方程
设
那么有
要有解要被整除
$t ≡ (c-a)’(b’^{-1})(mod \dfrac{d}{(b,d)})t0 = (c-a)’(b’^{-1})t ≡ t0(\bmod \dfrac{d}{(d,b)}) t ≡ t0(\bmod \dfrac{d}{g})x ≡ bt0+a(\bmod \dfrac{bd}{g})$
得出
然后下一条方程,
可以解决模数不互质的情况
即在不互质的情况下,要么无解,要么在的意义下有唯一解
.
先连立前两个方程
在联立
代入
再将带回去
在联立③④:
令代入③得:
再将带回去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| #include<bits/stdc++.h> using namespace std; typedef long long ll; ll n; ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x = 1,y = 0; return a; } ll d = exgcd(b,a%b,y,x); y -= (a/b)*x; return d; }
void merge(ll &a,ll&b,ll c,ll d) {
if(a==-1&&b==-1)return; ll x,y; ll g = exgcd(b,d,x,y); if((c-a)%g!=0) { a = b = -1; return; } d/=g; ll t0 = ((c-a)/g)%d*x%d; if(t0<0)t0+=d; a = b*t0+a; b = b*d; }
void solve() { cin>>n; ll a = 0,b = 1; for(int i = 1;i<=n;i++) { int c,d; cin>>c>>d; merge(a,b,c,d); } cout<<a<<endl; }
int main() { int t; cin>>t; while(t--) { solve(); } return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include<bits/stdc++.h> using namespace std; typedef __int128 ll; ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x = 1,y = 0; return a; } ll d = exgcd(b,a%b,y,x); y -= (a/b)*x; return d; }
ll mul(ll a,ll b,ll m) { ll ans = 0; while(b) { if(b&1) { ans = (ans+a)%m; } (a<<=1)%=m; b>>=1; } return ans; }
void merge(ll &a,ll &b,ll c,ll d) { if(a==-1&&b==-1)return; ll x, y; ll g = exgcd(b,d,x,y); if((c-a)%g) { a = b = -1; return; } ll t0 = ((c-a)/g)%(d/g)*x%(d/g); if(t0<0)t0+=(d/g); a = a+t0*b; b = b*d/g; }
int main() { int n; cin>>n; ll a = 0,b = 1,c,d; for(int i = 1;i<=n;i++) { long long _c,_d; cin>>_d>>_c; d = _d,c = _c; merge(a,b,c,d); } cout<<(long long)a<<endl; return 0; }
|
一共组数据,每组数据,给定个方程,
。判断方程是不是有解。
x mod 合数 = a ==>等价于模数是素数幂的方程,再去判断有没有解,我们还是用中国剩余定理
模数按照素数的幂次分类,比如(*)式看成一类,我们只需要看其中指数最高的那个方程,但我们还是要判前面满不满足的,比如我们既有一个方程又有个方程这样也是不行的
转化:
对合数取模
|
CRT
↓
对素数幂取模
即:
|
CRT
↓
然后对于每个素数幂的我们都check一下是否都是满足的
|
CRT
↓
若满足,则有解
总结:对合数取模 <=CRT 是桥梁=> 对素数幂取模
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std; typedef long long ll; bool solve() { int n; cin>>n; map<int,vector<pair<int,int>>>eqns;
for(int i = 1;i<=n;i++) { int a,m; cin>>a>>m; for(int j = 2;j*j<=m;j++) { if(m%j==0) { int p = j,pe = 1; while(m%j==0) m/=j,pe*=j; eqns[p].push_back({pe,a%pe}); } } if(m>1)eqns[m].push_back({m,a%m}); } for(auto eq:eqns) { auto eqn = eq.second; int v = max_element(eqn.begin(),eqn.end())->second; for(auto p:eqn) { if(v%p.first!=p.second)return false; } } return true; }
int main() { int t; cin>>t; while(t--) { if(solve()) cout<<"Yes\n"; else cout<<"No\n"; } return 0; }
|