洛谷 P2421 [NOI2002]荒岛野人

克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi个洞住下来。

每个野人i有一个寿命值Li,即生存的年数。

奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?

根据题目,设(x)为相遇天数,可以得到式子

[c_{i}+p_{i} imes xequiv c_{j}+p_{j} imes x(mod M) (1le i,jle N) ]

我们要保证对于每一对((i,j)),这个式子都要无解或者求出来的(x>min(l[i],[j])),即在两个人有一个人死了之后相遇

把这个式子化一下,就变成了

[(p_i-p_j) imes xequiv c_j-c_i(mod M) ]

写成方程的形式

[(p_i-p_j) imes x+M imes y=c_j-c_i ]

怎么解呢,我们不妨设(a=p_i-p_j,b=M,c=c_i-c_j)

方程就成了

[ax+by=c ]

(g=gcd(a,b)),当(g|c)时,设(a'=a/g,b'=b/g,c'=c/g)

我们可以用(exgcd),即扩展欧几里得算发求出(a'x'+b'y'=1)的一组解(x',y')

两边乘(c'),得到(a'c'x'+b'c'y'=c')

两边乘(g),得到(a'gc'x'+b'gc'y'=c'g)

化简得到(ac'x'+bc'y'=c)

于是我们得到了方程一组解(x_0=x'c',y_0=y'c')

因为(M)最大到(10^6),所以从(Max_{i=1}^{n}c_i-10^6)枚举(M),如果方程都无解或者(x>min(l[i],[j])),那这个(M)就是答案了

Code

#include <iostream>
#include <cstdio>
#define N 1000000
#define int long long
using namespace std;
int n,d[500],p[500],l[500],mm;
int gcd(int x,int y)
{
	if (!y)return x;
	return gcd(y,x%y);
}
void exgcd(int a,int b,int &x,int &y)
{
	if (!b)x=1,y=0;
	else
	{
		exgcd(b,a%b,x,y);
		int t=x;
		x=y;
		y=t-a/b*y;
	}
}
int check(int m)
{
	for (int i=1;i<n;i++)
		for (int j=i+1;j<=n;j++)
		{
			int a=p[i]-p[j],b=m,c=d[j]-d[i],g=gcd(a,b),x,y;
			if (c%g!=0)continue;
			a/=g,b/=g,c/=g;	
			exgcd(a,b,x,y);
			if (b<0)b=-b;			
			x=(x*c%b+b)%b;
			if (x<=min(l[i],l[j]))return 0;
		}
	return 1;
}
signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
		cin>>d[i]>>p[i]>>l[i],mm=max(mm,d[i]);
	for (int i=mm;i<=N;i++)
	{
		if (check(i))
		{
			cout<<i<<endl;
			return 0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/sdlang/p/13068075.html