[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 }

 

posted @ 2017-12-15 20:43  skylee03  阅读(100)  评论(0编辑  收藏  举报