Typesetting math: 100%

扩展中国剩余定理

扩展中国剩余定理

前置芝士

中国剩余定理

作用

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

推导过程

k1 个方程解为 x ,令 m=i=1k1mi注:此处不是乘积,而是 lcm(m1,m2,...,mk1)

我们有 x+im(iZ) 为前 k1 个方程的通解

对于 k 个方程,求:

tZ,x+tmak (mod m)

可化为:

tmakx (mod mk)

由裴蜀定理得,方程若有解,则 gcd(m,mk)|akx

通过 exgcd ,我们可以求得:

tm+bmk=gcd(m,mk)

的一组通解

两边同乘 akxgcd(m,mk) ,得:

tmakxgcd(m,mk)+bmkakxgcd(m,mk)=akx

由数学归纳法,循环 n1 次即可

#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\n",(long long)ans);
	return 0;
}

例题

P4774 [NOI2018]屠龙勇士

思路

x 为前 i1 个的答案,则这个方程应为:

b[i](x+tM)a[i] (mod p)

稍加转移,可得

tb[i]M+vp=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\n",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\n",ExCRT()); 
	} 
} 

(大力感谢AlanSP

posted @   jasony_sam  阅读(150)  评论(0编辑  收藏  举报
编辑推荐:
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
阅读排行:
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· Vite CVE-2025-30208 安全漏洞
· 《HelloGitHub》第 108 期
· MQ 如何保证数据一致性?
· 一个基于 .NET 开源免费的异地组网和内网穿透工具
点击右上角即可分享
微信分享提示