[POI2010]Frog
题目大意:
一个数轴上有n个点,现在你要在这些点上跳。
每次跳的时候你只能跳到离这个点第k近的点上,而且要连续跳m次。
问从每一个点出发,最后分别会在哪一个点结束。
思路:
首先可以维护一个大小为k+1的队列,使得离当前点第k远的点一定在这个队列中。
显然当i=1时,队列的范围是[1,k+1],然后当pos[i]-pos[l]>pos[l+k+1]-pos[i]时,队列往右移一个点。
至于第k近的点,则要么是左端点,要么是右端点,只要比一下哪个更远即可。
这样就可以O(n)地求出离每个点第k近的点。
但是现在我们要跳m次,而m很大,很显然不能暴力跳,而用LCA之类的也不行,因为到最顶上是一个环。
考虑把每一次跳的过程看作一个置换,那么总共要进行m次置换。
而这些置换可以通过类似于快速幂的方法实现。
把所有的点置换一次是O(n)的,总共会有O(log m)次置换,所以时间复杂度是O(n log m)的。
1 #include<cstdio> 2 #include<cctype> 3 typedef long long int64; 4 inline int64 getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int64 x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int N=1000001; 12 int64 pos[N]; 13 int next[N],tmp[N],ans[N]; 14 int main() { 15 const int n=getint(),k=getint(); 16 register int64 m=getint(); 17 for(register int i=1;i<=n;i++) pos[i]=getint(); 18 for(register int i=1,l=1;i<=n;i++) { 19 while(l+k<n&&pos[i]-pos[l]>pos[l+k+1]-pos[i]) l++; 20 next[i]=(pos[i]-pos[l]>=pos[l+k]-pos[i])?l:l+k; 21 } 22 for(register int i=1;i<=n;i++) ans[i]=i; 23 for(;m;m>>=1) { 24 if(m&1) { 25 for(register int i=1;i<=n;i++) { 26 ans[i]=next[ans[i]]; 27 } 28 } 29 for(register int i=1;i<=n;i++) tmp[i]=next[next[i]]; 30 for(register int i=1;i<=n;i++) next[i]=tmp[i]; 31 } 32 for(register int i=1;i<=n;i++) { 33 printf("%d%c",ans[i]," \n"[i==n]); 34 } 35 return 0; 36 }