题意:有长度为n的环,环上有m个坏点,目标在z位置,从1出发,每次跳k步,最后到达z,问最小的k是多少。
题解:1、暴力枚举,我没试过,但有人这样过了。
2、考虑目标z,假设跳了m圈,最后跳到z,则有(z-1+m*n)≡0 (mod k),如果(z-1)%k==0,直接检验小于z的坏点是否会猜到,否则,m*n≡(z-1)%k (mod k),用模线性方程可以求出m,考虑坏点,分为两类,一类坏点的坐标小于z,人会从上面跨过m+1次,另一类坏点坐标大于z,人会从上面跨过m次。
对于任意一个坏点a,如果在某一次踩在了它上面,就存在(a-1)*(a-1+n)*(a-1+2*n)*(a-1+3*n)....(a-1+m*n)≡0 (mod k),反之,就是存在a-1+k*n≡0 (mod k),可以用上面方法求出k,如果坏点属于第一类,则k必须大于m,否则k大于等于m,然后枚举k在依次验证。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 inline int gcd(int a,int b) 6 { 7 if(b==0) 8 return a; 9 else 10 return gcd(b,a%b); 11 } 12 inline int extgcd(int a,int b,int &x,int &y) 13 { 14 if (b==0) 15 { 16 x=1,y=0; 17 return a; 18 } 19 int d, tp; 20 d=extgcd (b, a%b, x, y); 21 tp=x; 22 x=y; 23 y=tp - a/b*y; 24 return d; 25 } 26 inline int getx(int a, int b, int n) // ! n > 0 27 { 28 int e, i, d, x, y; 29 d = extgcd(a, n, x, y); 30 if (b%d>0) return -1; 31 else 32 { 33 e=(x*(b/d))%n; 34 int ans=e,t=n/d,num=(n-e)/t; 35 for(i=-1;i<=1;i++) 36 e=(e+(num+i)*t+n)%n,ans=min(ans,e); 37 return ans; 38 } 39 } 40 int s1[1005],s2[1005],top1,top2; 41 int main() 42 { 43 int n,z,m; 44 while(scanf("%d%d%d",&n,&z,&m)!=EOF) 45 { 46 int i,k,v; 47 top1=top2=0; 48 for(int i=0; i<m; i++) 49 { 50 scanf("%d",&v); 51 if(v<z) 52 s1[top1++]=v; 53 else 54 s2[top2++]=v; 55 } 56 if(top1==0) 57 { 58 printf("1\n"); 59 continue; 60 } 61 for(k=2; k<z; k++) 62 { 63 int b,d=gcd(k,z-1); 64 if(d==k) 65 { 66 for(i=0; i<top1; i++) 67 if((s1[i]-1)%k==0) 68 break; 69 if(i<top1) 70 continue; 71 printf("%d\n",k); 72 break; 73 } 74 m=getx(n,k-(z-1)%k,k); 75 if(m==-1) 76 continue; 77 for(i=0; i<top1; i++) 78 if((s1[i]-1)%k==0||((v=getx(n,k-(s1[i]-1)%k,k))!=-1&&v<=m)) 79 break; 80 if(i<top1) 81 continue; 82 for(i=0; i<top2; i++) 83 if((s2[i]-1)%k==0||((v=getx(n,k-(s2[i]-1)%k,k))!=-1&&v<m)) 84 break; 85 if(i<top2) 86 continue; 87 printf("%d\n",k); 88 break; 89 } 90 } 91 return 0; 92 }