洛谷 P2421 [NOI2002]荒岛野人
克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi个洞住下来。
每个野人i有一个寿命值Li,即生存的年数。
奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?
根据题目,设\(x\)为相遇天数,可以得到式子
\[c_{i}+p_{i}\times x\equiv c_{j}+p_{j}\times x(mod\ M)\ (1\le i,j\le N)
\]
我们要保证对于每一对\((i,j)\),这个式子都要无解或者求出来的\(x>min(l[i],[j])\),即在两个人有一个人死了之后相遇
把这个式子化一下,就变成了
\[(p_i-p_j)\times x\equiv c_j-c_i(mod\ M)
\]
写成方程的形式
\[(p_i-p_j)\times x+M\times 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;
}