人生第一次作NOI的题祭!!!html
大概是NOI最简单的一道题ios
克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞C1,C2,…,CN中,之后每一年,第i个野人会沿顺时针向前走Pi个洞住下来。c++
每一个野人i有一个寿命值Li,即生存的年数。git
下面四幅图描述了一个有6个山洞,住有三个野人的岛上前四年的状况。三个野人初始的洞穴编号依次为1,2,3;每一年要走过的洞穴数依次为3,7,2;寿命值依次为4,3,1。spa
(图片就不考了)code
咱们发现这道题奇怪的给了答案的数据规模,这就说明咱们能够枚举答案。htm
而后咱们发现n的数据规模很小,n^2*m彷佛能够过。blog
咱们对于每个答案,枚举任意两我的是否不会相遇。图片
对于每两个野人,咱们能够根据他们的C和P列出同余方程:get
Ci+Pi*x≡Cj+Pj*x(mod b) 其中x是年数,b是枚举的答案。
而后,将这个同余方程转化为不定方程,而后求解。
Ci+Pi*x=Cj+Pj*x+b*-y(-y是一个构造的新的未知数)
(Pi-Pj)*x+b*y=Cj-Ci
而后用拓展欧几里得求解(这是解析)求解
最后必定要注意!!!(由于在这里卡了几个小时)求完特解之后,要保证最终解为正。
一般状况,只须要(x*(Cj-Ci)/d(最小公约数)+b/d)%b/d就能够了。
可是注意Cj-Ci可能为负,并且c++的负数取模的机制是取决于被模数,被模数为正则余数为正,为负则反之。
而由于Cj-Ci可能为负,可能致使最终答案依然为负。
故此,咱们将x*(Cj-Ci)/d先模b/d,保证其绝对值是小于b/d的,可是,b/d也可能为负(由于d可能为负),因此即便加上一个b/d也不能保证结果为正。
所以,咱们把最后加上的b/d变成一个它的倍数且保证它为正的,能够替换为b或者b/d的绝对值。
代码以下:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #define MAXN 10010 #define in(a) a=read() #define REP(i,k,n) for(int i=k;i<=n;i++) using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n,m; int c[100010],p[100010],l[100010]; int t; inline void exgcd(int a,int b,int &d,int &x,int &y){ if(b==0) x=1,y=0,d=a; else exgcd(b,a%b,d,x,y),t=x,x=y,y=t-a/b*y; } inline bool check(int k){ REP(i,1,n) REP(j,i+1,n){ int a=p[i]-p[j],b=k,x,y,d; exgcd(a,b,d,x,y); if((c[j]-c[i])%d!=0) continue; x=((x*(c[j]-c[i])/d)%(b/d)+(abs(b/d)))%(b/d);//保证x为正 if(x<=min(l[i],l[j])) return 0; } return 1; } int main(){ in(n); REP(i,1,n) in(c[i]),in(p[i]),in(l[i]),m=max(m,c[i]); REP(i,m,1000000) if(check(i)){ cout<<i<<endl; return 0;} return 0; }