#扩欧,同余方程#JZOJ 1158 洛谷 2421 荒岛野人

比赛


题目

N N N个野人,一开始依次住在山洞 C 1 , C 2 , … , C N C_1,C_2,\dots,C_N C1,C2,,CN中,第 i i i个野人会沿顺时针向前走 P i P_i Pi个洞住下来。每个野人 i i i有一个寿命值 L L L,至少要多少个山洞才能使没有任何两个野人在有生之年处在同一个山洞。


分析

枚举山洞的数量,计算出(山洞的数量为 p p p
C i + P i × x ≡ C j + P j × x ( m o d p ) C_i+P_i\times x≡C_j+P_j\times x(modp) Ci+Pi×xCj+Pj×x(modp)
移项后得到
P i × x − P j × x ≡ C j − C i ( m o d p ) P_i\times x-P_j\times x≡C_j-C_i(mod p) Pi×xPj×xCjCi(modp)
C j − C i C_j-C_i CjCi减去 p × y p\times y p×y得到 ( P i − P j ) × x (P_i-P_j)\times x (PiPj)×x可得
( P i − P j ) × x = C j − C i − p × y (P_i-P_j)\times x=C_j-C_i-p\times y (PiPj)×x=CjCip×y
移项后
( P i − P j ) × x + p × y = C j − C i (P_i-P_j)\times x+p\times y=C_j-C_i (PiPj)×x+p×y=CjCi
扩欧找到 x x x的答案
所以x的最小整数解是通过 ( P i − P j ) × x + p × y = g c d ( P i − P j , p ) (P_i-P_j)\times x+p\times y=gcd(P_i-P_j,p) (PiPj)×x+p×y=gcd(PiPj,p)得到的
最后 x = x ∗ ( C j − C i ) ÷ g c d ( P i − P j , p ) m o d p x=x*(C_j-Ci)\div gcd(P_i-P_j,p)mod p x=x(CjCi)÷gcd(PiPj,p)modp
x x x同时在两个野人的有生之年中,那么继续枚举,否则直接输出山洞的数量。


代码

#include <cstdio>
#include <cctype>
using namespace std;
int n,c[16],p[16],l[16],cave;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int max(int a,int b){return (a>b)?a:b;}
int exgcd(int a,int b,int &x,int &y){
	if (b==0) {x=1;y=0; return a;}
	else{
		int d=exgcd(b,a%b,y,x);
		y-=a/b*x; return d;
	}
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++) cave=max(cave,c[i]=in()),p[i]=in(),l[i]=in();
	while (1){
		bool flag=0; int d,x,y,a,b;
		for (int i=1;i<n;i++){
		for (int j=i+1;j<=n;j++){
			a=p[i]-p[j];b=c[j]-c[i];
			if (a<0) a=-a,b=-b;
			d=exgcd(a,cave,x,y); //扩欧
			int cav=cave/d;
			x=(x*b/d%cav+cav)%cav;
			if (!x) x+=cav;
			if (b%d==0&&x<=l[i]&&x<=l[j]) flag=1;//有生之年不会和平
			if (flag) break;
		    }if (flag) break;
		}
		if (flag) cave++;
		else return !printf("%d",cave);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值