扩展中国剩余定理 excrt

题面

{xa1(modp1)xa2(modp2)xan(modpn)

关于 x 的方程组的最小非负整数解

前言

excrt 就是用来求解这样的同余方程组的

crt 完全可以被 excrt 替代,本蒟蒻不会

正文

先拎出来两个方程看

{xa1(modp1)xa2(modp2)

转化一下

x=a1+n1p1=a2+n2p2

n1p1n2p2=a2a1

由裴属定理 n1p1n2p2=(p2,p1)

条件是 (p2,p1)|a2a1

由于负数对 gcd 无影响,所以就是 n1p1n2p2=(p1,p2)

上面那个式子用 exgcd 求一下

不会 exgcd 的大佬们请点这里 (学完别忘了回来学 excrt 鸭)

所以就能求出上式的 n1 n2

但你现在求出的数其实要 ×(a2a1)/(p1,p2) 才是 n1

(由于上面的裴属定理)

那么我们现在求出了 n1

x0=a1+n1p1

就又多了一组方程

那么 xx0(modlcm(p1,p2))

用这个方程和第 3 个方程消,以此类推

最后的 x0 就是答案!!!

代码

#include <bits/stdc++.h>
using namespace std;
#define int __int128
const int N=1e5+5;
int n,x,y,gcd,lcm;
struct node{int p,a;}e[N];
void exgcd(int a,int b){
    if(b==0){
        gcd=a,x=1,y=0;
        return;
    }
    exgcd(b,a%b);
    int o=x;
    x=y;
    y=o-a/b*y; 
}
node excrt(node o1,node o2){
    int a1=o1.a,a2=o2.a,p1=o1.p,p2=o2.p,x0;
    exgcd(p1,p2);
    //cout<<x<<endl;
    assert((a2-a1)%gcd==0);
    int c=(a2-a1)/gcd;
    lcm=p1*p2/gcd;
    x0=(x*p1*c+a1)%lcm;
    if(x0<0)x0+=lcm;
    return node{lcm,x0};
}
int read() {long long t; cin>>t; return t;}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        e[i].p=read(),e[i].a=read();
    }
    if(n==1){cout<<(long long)e[1].a;return 0;}
    node ans=excrt(e[1],e[2]);  
    //cout<<ans.a<<" "<<ans.p<<endl;
    for(int i=3;i<=n;i++){
        ans=excrt(ans,e[i]);
    }
    cout<<(long long)ans.a;
    return 0;
}

不会 & 的写法(至少现在不会了)

如果非要看的话请看本蒟蒻的早年代码

#include <bits/stdc++.h>
using namespace std;
#define int __int128
const int N=1e5+5;
int n;
struct node{
	int b,a;
}ans,s[N];
int read() {long long t; cin>>t; return t;}
void exgcd(int a,int b,int &x,int &y,int &g)
{
    if(!b) {x=1,y=0,g=a;return ;}
    int x0,y0;
    exgcd(b,a%b,x0,y0,g);
    x=y0,y=x0-(a/b)*y0; 
}
node excrt(node fi,node se)
{
    int a1=fi.a,a2=se.a,b1=fi.b,b2=se.b,q1,q2,g,c,x,lcm;
    exgcd(a1,a2,q1,q2,g);
	cout<<(long long)q1<<endl;
    assert((b2-b1)%g==0);
    c=(b2-b1)/g;
    lcm=a1/g*a2;
    x=(q1*c*a1+b1)%lcm;
    if(x<0) x+=lcm;
    return (node){x,lcm};
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		s[i].a=read();
		s[i].b=read();
	}
	if(n==1)cout<<(long long)s[1].b<<endl,exit(0);
	ans=excrt(s[1],s[2]);
	cout<<(long long)ans.b<<" "<<(long long)ans.a<<endl;
	for(int i=3;i<=n;i++){
		ans=excrt(ans,s[i]);
	}
	cout<<(long long)ans.b;
	return 0;
}

后记

吃饭时空出了 3 个位子

回忆……

小小水元素充盈一下

posted @   小惰惰  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
/* 鼠标点击求赞文字特效 */
点击右上角即可分享
微信分享提示

目录导航