1. 程式人生 > 其它 >[NOI2002] 荒島野人

[NOI2002] 荒島野人

link

二十年前的NOI是如此淳樸……

題並不難,主要是我對擴歐理解不深,再加上許久沒寫了,於是乎寫得一塌糊塗……

考慮列舉洞穴個數 \(k\) ,並驗證個數是否合法,很明顯可以列出式子(假如兩個野人 \(s\) 年後相遇):

\[c_1+p_1s\equiv c_2+p_2s\pmod{k} \]

變形可得:

\[(p_1-p_2)s-rk=c_2-c_1 \]

可以發現這就是一個 \(a=p_1-p_2,b=k,c=c_2-c_1\) 的不定方程,需要判斷的就是 \(x\) 是否比兩個人的壽命都要短。

首先可以肯定的是 \(c\equiv0\pmod{\gcd(a,b)}\) 不成立時方程無解,那麼野人永遠不可能相遇,顯然合法,接下來就考慮如何處理其他情況:

假設 \(g=\gcd(a,b)\) ,那麼方程可以轉化為 \(\frac{a}{g}x+\frac{b}{g}y=\frac{c}{g}\) ,其中 \(\gcd(a',b')=1\)

\[a'x_0+b'y_0=1 \]

此時應該先利用\(x_0,b'\)求出最小整數解之後再用以下公式。注意 \(b'<0\) 時要取相反數,否則求出來的東西會是負的。

\[a'(x_0c')+b'(y_0c')=c' \]

最終的答案就是 \(x_0c'\) 。別的沒什麼了,就是要保證洞穴數要大於最大編號。

#include<cstdio>
#define zczc
#define int long long
const int N=105;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,c[N],p[N],l[N];

int x,y;
inline int exgcd(int a,int b){
	if(b==0){x=1,y=0;return a;}
	int re=exgcd(b,a%b);
	int xx=x,yy=y;
	x=yy,y=xx-a/b*yy;
	return re;
}
bool check(int wh){
	for(int i=1;i<m;i++){
		if(wh<c[i]-1)return false;
		for(int j=i+1;j<=m;j++){
			if(wh<c[j]-1)return false;
			int a=p[i]-p[j],b=wh,cc=c[j]-c[i];
			int g=exgcd(a,b);
			if(cc%g)continue;
			a/=g,b/=g,cc/=g;
			if(b<0)b=-b;
			x=(x*cc%b+b)%b;
			if(x<=l[i]&&x<=l[j])return false;
		}
	}
	return true;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);
	for(int i=1;i<=m;i++){
		read(c[i]);read(p[i]);read(l[i]);
	}
	for(int i=1;;i++){
		if(check(i)){
			printf("%lld\n",i);
			return 0;
		}
	}
	
	return 0;
}