P2421 [NOI2002]荒岛野人
暴力非常好写,按题目模拟即可。这样经过测试有40分。
正解做法:
题目上的条件可以转化为找一个山洞数,使下面的式子无解。
接着移项,可以变为
典型的同余方程,然后因为题目中的n极小,所以可以n^2暴力判断。
枚举的时候注意初始值为c[i]的最大值,然后枚举判断,无解的情况有两种:
1.在有生之年内都不会在一个山洞中,也就是该方程的最小正整数解>t值。
2.本来这个方程就无解。
还有,求得的gcd可能为负值,要变为相反数再求解最小正整数解。
代码(40分暴力):
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; int n; int save[maxn],c[maxn],p[maxn],t[maxn],life[maxn]; int maxx; int tot; bool flag; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&c[i],&p[i],&t[i]); maxx=max(c[i],maxx); life[i]=t[i]; save[i]=c[i]; } for(int i=maxx;;i++){ flag=false; tot=0; memcpy(c,save,sizeof(c)); memcpy(t,life,sizeof(t)); while(1){ for(int j=1;j<=n;j++){ int save=c[j]; if(t[j]>0){ c[j]+=p[j]; if(c[j]>i) c[j]%=i; if(!c[j]) c[j]=save; } } for(int j=1;j<=n;j++){ for(int k=j+1;k<=n;k++){ if(c[j]==c[k]&&t[j]>0&&t[k]>0){ flag=true; break; } } } for(int j=1;j<=n;j++) t[j]--; for(int j=1;j<=n;j++) if(!t[j]) tot++; if(flag||tot==n) break; } if(!flag){ printf("%d\n",i); return 0; } } return 0; }
正解(exgcd):
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; int n; int c[maxn],p[maxn],t[maxn]; long long x,y; int maxx; long long exgcd(long long a,long long &x,long long b,long long &y){ if(!b){ x=1; y=0; return a; } long long gcd=exgcd(b,x,a%b,y); long long tmp=x; x=y; y=tmp-(a/b)*x; return gcd; } bool check(long long ans){ for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ long long a=p[i]-p[j],b=ans,cc=c[j]-c[i]; long long gcd=exgcd(a,x,b,y); if(cc%gcd) continue; if(b/gcd<0) b=-b;//注意有负数的情况 x=((x*cc/gcd)%(b/gcd)+(b/gcd))%(b/gcd); if(x<=t[i]&&x<=t[j]) return false; } } return true; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&c[i],&p[i],&t[i]); maxx=max(c[i],maxx); } for(int i=maxx;;i++){ if(check(i)){ printf("%d\n",i); return 0; } } return 0; }