[NOI2022]荒島野人(exgcd)
現在看來一道很水的\(exgcd\)在當年竟是一道\(NOI\)題目。
原題面
題目描述
克里特島以野人群居而著稱。島上有排列成環行的\(m\)個山洞。這些山洞順時針編號為\(1,2,\cdots,m\)。島上住著\(n\) 個野人,一開始依次住在山洞\(C_1,C_2,\cdots,C_n\)中,以後每年,第\(i\)個野人會沿順時針向前走\(P_i\)個洞住下來。
每個野人\(i\)有一個壽命值\(L_i\),即生存的年數。
下面四幅圖描述了一個有\(6\)個山洞,住有三個野人的島上前四年的情況。三個野人初始的洞穴編號依次為\(1,2,3;\)每年要走過的洞穴數依次為 \(3,7,2;\)
奇怪的是,雖然野人有很多,但沒有任何兩個野人在有生之年處在同一個山洞中,使得小島一直保持和平與寧靜,這讓科學家們很是驚奇。他們想知道,至少有多少個山洞,才能維持島上的和平呢?
輸入格式
第\(n\)行為一個整數\(n(1\leq n\leq 15)\),即野人的數目。
第\(2\)行到第\(N+1\)每行為三個整數\(C_i, P_i, L_i (1\leq C_i,P_i \leq 100, 0\leq L_i\leq 10^6 )\),表示每個野人所住的初始洞穴編號,每年走過的洞穴數及壽命值。
輸出格式
僅包含一個數\(M\),即最少可能的山洞數。輸入資料保證有解,且 \(M\)
樣例輸入
3
1 3 4
2 7 3
3 2 1
樣例輸出
6
說明/提示
\(1\le N\le15\),\(1\leq C_i,P_i\leq 100\),\(0\leq L_i\leq 10^6\)
保證\(M\le10^6\)
題解
我們從小到大列舉答案,我們再列舉兩個不同的野人\(i,j\),我們判斷\(i,j\)在壽命值範圍內能否相遇,能就說明列舉的答案不合法,如果所有\(i,j\)都不能相遇,那就說明合法,輸出答案結束程式。
如何判斷
我們現在的問題是如何判斷他們能否相遇。
\(\forall i,j\)設\(a=P_i-P_j\)即一年\(i\)比\(j\)多走的步數,\(x\)
這樣就可以用擴充套件歐幾里德來解決了,時間複雜度是\(O(MN^2\log M)\)可以通過本體。
code
#include<bits/stdc++.h>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
int n,c[25],p[25],l[25],w;
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;
}
int d=exgcd(b,a%b,y,x);
y=y-a/b*x;
return d;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&c[i],&p[i],&l[i]);
w=max(w,c[i]);
}
for(int d,x,y,a,b;;w++)
{
bool flag=0;
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,w,x,y);
int m=w/d;
x=(x*b/d%m+m)%m;
if(!x)x+=m;
if(b%d==0&&x<=l[i]&&x<=l[j])flag=1;
if(flag)break;
}
if(flag)break;
}
if(!flag){printf("%d",w);return 0;}
}
return 0;
}