bzoj 2093: [Poi2010]Frog

2093: [Poi2010]Frog

Description

一个条河无限宽,上面有n块石头,石头离左边的河岸(无限宽,右边河岸不晓得在哪)距离严格递增,现在Zxl想锻炼自己的跳跃能力(谁叫他在班里外号是鸟怪。。畸形),他在某一块石头上,想跳到离他这块石头第k远的石头上去,假如离他第k远的石头不是唯一的,他就选离岸最近的那一个(不然回不去了),他想你让他知道,从每块石头开始跳了m次后,自己在哪。

Input

第一行有3个由空格隔开的整数n, k (n, k <= 1,000,000), m (m <= 10^18)。
第二行有n个正整数,第i个数表示第i块石头离左岸的距离,保证输入的n个正整数严格递增,并且不超过10^18。

Output

一行n个由空格隔开的整数,第i个表示Zxl从第i块石头开始跳,跳m次后会在哪个石头上。

Sample Input

5 2 4
1 2 4 7 10

Sample Output

1 1 3 1 1
 
题解:
 
思路很不错的一道题。。。
首先考虑如何把这个第k远的点求出来,想了半天完全没有思路,啊啊啊啊啊。。
后来看了题解才知道要维护一个[l,l+k]的区间,答案肯定是左右中的一个(太神了%%%%)
那么中心点在移动的时候,如果a[i]-a[l]>a[r+1]-a[i],那么区间就可以往右移了,因为r+1这个点已经比l优了。。
接下来就最终位置的方法也是十分巧妙的,用了倍增的思想,类似于快速幂。
#include<stdio.h>
#include<iostream>
using namespace std;
const int N=1000005;
int n,k,l,r,i,ans[N],f[N],F[N];
long long m,a[N];
inline void read(long long &v){
    char ch,fu=0;
    for(ch='*'; (ch<'0'||ch>'9')&&ch!='-'; ch=getchar());
    if(ch=='-') fu=1, ch=getchar();
    for(v=0; ch>='0'&&ch<='9'; ch=getchar()) v=v*10+ch-'0';
    if(fu) v=-v;
}
int main()
{
    scanf("%d%d",&n,&k),read(m);
    for(i=1;i<=n;i++) read(a[i]);
    l=1;r=k+1;f[1]=r;
    for(i=2;i<=n;i++)
    {
        while(r<n&&a[i]-a[l]>a[r+1]-a[i]) l++,r++;
        if(a[i]-a[l]>=a[r]-a[i]) f[i]=l;else f[i]=r;
    }
    for(i=1;i<=n;i++) ans[i]=i;
    while(m)
    {
        if(m&1)
        {
            for(i=1;i<=n;i++) ans[i]=f[ans[i]];
        }
        for(i=1;i<=n;i++) F[i]=f[f[i]];
        for(i=1;i<=n;i++) f[i]=F[i];
        m>>=1;
    }
    for(i=1;i<n;i++)
        printf("%d ",ans[i]);
    printf("%d",ans[n]);
    return 0;
}

  

 

 
posted @ 2016-07-11 19:08  lwq12138  阅读(368)  评论(0编辑  收藏  举报