• 扩展中国剩余定理


    扩展中国剩余定理

    前置芝士

    中国剩余定理

    作用

    求同余方程余数无限制的通解。

    推导过程

    (k-1) 个方程解为 (x) ,令 (m=prod_{i=1}^{k-1}m_i)注:此处不是乘积,而是 (lcm(m_1,m_2,...,m_{k-1}))

    我们有 (x+i*m(iin Z)) 为前 (k-1) 个方程的通解

    对于 (k) 个方程,求:

    [tin Z,x+t*mequiv a_k~(mod~m) ]

    可化为:

    [t*mequiv a_k-x~(mod~m_k) ]

    由裴蜀定理得,方程若有解,则 (gcd(m,m_k)|a_k-x)

    通过 (exgcd) ,我们可以求得:

    [t*m+b*m_k=gcd(m,m_k) ]

    的一组通解

    两边同乘 (dfrac{a_k-x}{gcd(m,m_k)}) ,得:

    [t*m*frac{a_k-x}{gcd(m,m_k)}+b*m_k*frac{a_k-x}{gcd(m,m_k)}=a_k-x ]

    由数学归纳法,循环 (n-1) 次即可

    #include<bits/stdc++.h>
    using namespace std;
    typedef __int128 ll;
    const int N=1e5+9;
    ll n,m,ans,x,y,M;
    l'l a[N],b[N];
    
    inline ll exgcd(ll a,ll b,ll &x,ll &y){
    	if(!b) {x=1;y=0;return a;}
    	ll ret=exgcd(b,a%b,x,y);
    	ll z=x;x=y,y=z-(a/b)*y;
    	return ret;
    }
    
    int main(){
    	scanf("%lld",&n);
    	for(int i=1;i<=n;i++) scanf("%lld%lld",&b[i],&a[i]);
    	ans=a[1],M=b[1];
    	for(int i=2;i<=n;i++)
    	{
    		ll tem=((a[i]-ans)%b[i]+b[i])%b[i];
    		ll G=exgcd(M,b[i],x,y);
    		x=x%b[i]*(tem/G);
    		ans=ans+M*x;
    		M=M*b[i]/G;//gcd(a,b)*lcm(a,b)=a*b
    		ans=(ans+M)%M;
    	}
    	printf("%lld
    ",(long long)ans);
    	return 0;
    }
    

    例题

    P4774 [NOI2018]屠龙勇士

    思路

    (x) 为前 (i-1) 个的答案,则这个方程应为:

    [b[i](x+t*M)equiv a[i]~(mod~p) ]

    稍加转移,可得

    [t*b[i]*M+v*p=a[i]-b[i]*x ]

    然后用扩欧求解,往下递推

    代码
    #include<bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    const int maxn=100005; 
    
    int T,n,m,b[maxn],t[maxn];
    ll a[maxn],p[maxn],mx;
    multiset<ll>s;
    
    inline void exgcd(ll A,ll B,ll &x,ll &y,ll &gcd){
    	if(!B) x=1,y=0,gcd=A;
    	else exgcd(B,A%B,y,x,gcd),y-=(A/B)*x;
    }
    
    ll ExCRT(){
    	ll ans=0,lcm=1,x,y,gcd,A,B,C;
    	for(int i=1;i<=n;i++){
    		A=(__int128)b[i]*lcm%p[i];
    		B=p[i];
    		C=(a[i]-b[i]*ans%p[i]+p[i])%p[i];
    		exgcd(A,B,x,y,gcd);x=(x%B+B)%B;
    		if(C%gcd) return -1;//如果C不是gcd(A,B)的倍数,那就不用算了 
    		ans+=(__int128)(C/gcd)*x%(B/gcd)*lcm%(lcm*=B/gcd);
    		ans%=lcm;
    		//puts("----------");
    		//printf("%lld
    ",ans);
    	}
    	if(ans<mx) ans+=((mx-ans-1)/lcm+1)*lcm;//砍的刀数不能小于龙的血量除以攻击力的最大值 
    	return ans;
    }
    
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		s.clear();mx=0;
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    		for(int i=1;i<=n;i++) scanf("%lld",&p[i]);
    		for(int i=1;i<=n;i++) scanf("%d",&t[i]);
    		for(int i=1,x;i<=m;i++) scanf("%d",&x),s.insert(x);
    		for(int i=1;i<=n;i++) {
    			if(*s.begin()>a[i])b[i]=*s.begin(),s.erase(s.begin());
    			else b[i]=*(--s.upper_bound(a[i])),s.erase(s.lower_bound(b[i]));
    			s.insert(t[i]);//求龙的血量除以攻击力的最大值  
    			mx=max(mx,(a[i]-1)/b[i]+1);
    		}
    		//for(int i=1;i<=n;i++)
    		//	printf("%d ",(a[i]-1)/b[i]+1);
    		printf("%lld
    ",ExCRT()); 
    	} 
    } 
    

    (大力感谢AlanSP

  • 相关阅读:
    Lc1049_最后一块石头的重量II
    Lc343_整数拆分
    MySQL使用Limit关键字限制查询结果的数量效率问题
    Lc62_不同路径
    Java几种序列化方式对比
    3、你平时工作用过的JVM常用基本配置参数有哪些?
    2、你说你做过JVM调优和参数配置,请问如何盘点查看MM系统默认值
    强引用、软引用、弱引用、虚引用分别是什么?
    零拷贝
    并发编程面试题-锁的优化 和 happen-before原则
  • 原文地址:https://www.cnblogs.com/jasony/p/13377364.html
Copyright © 2020-2023  润新知