BZOJ2093[Poi2010] Frog

30 篇文章 0 订阅
23 篇文章 1 订阅

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

Solution:

自己对倍增的想法掌握的还不是很扎实啊…

首先,我们为什么要想到倍增:跳跃 m 次是一个重复的过程,因此对于这种问题我们就可以用倍增优化到log级别。顺便说一句,这题还卡空间,所以倍增数组只开两个。

既然想到用倍增求解后,问题就只剩下一个内容了:如何求离一个点第k近的点。

最开始想的就是二分,二分一个左端点,直接检验右端点是否可以,但是这样写起来效果并不好,代码很恶心

#include<stdio.h>
#include<ctype.h>
#include<iostream>
#define M 1000005
#define ll long long
using namespace std;
int d[2][M],ans[M];
ll A[M];
int min(int a,int b){return a<b?a:b;}
inline void Rd(ll &res){
    char c;res=0;
    while(c=getchar(),!isdigit(c));
    do{
        res=(res<<1)+(res<<3)+(c^48);
    }while(c=getchar(),isdigit(c));
}
int main(){
    int n,k,sz=0;ll m;
    scanf("%d %d",&n,&k);cin>>m;
    for(int i=1;i<=n;i++)Rd(A[i]);
    for(int i=1;i<=n;i++){
        int L=0,R=min(i-1,k),res;
        while(L<=R){
            int mid=(L+R)>>1;
            if(i+k-mid>n){
                L=mid+1;
                continue;
            }
            if(i+k-mid<n&&A[i+k-mid+1]-A[i]<A[i]-A[i-mid]){
                R=mid-1;
                continue;
            }
            if(i-mid>1&&A[i]-A[i-mid-1]<=A[i+k-mid]-A[i]){
                L=mid+1;
                continue;
            }
            res=mid;
            break;
        }
        d[0][i]=(A[i]-A[i-res]>=A[i+k-res]-A[i]?i-res:i+k-res);
    }
    int cur=0;
    for(int i=1;i<=n;i++)ans[i]=i;
    while(m){
        if(m&1){
            for(int i=1;i<=n;i++)
                ans[i]=d[cur][ans[i]];
        }
        for(int i=1;i<=n;i++)
            d[!cur][i]=d[cur][d[cur][i]];
        m>>=1,cur=!cur;
    }
    for(int i=1;i<=n;i++)
        printf("%d%c",ans[i],i==n?'\n':' ');
    return 0;
}

然后大犇给出了 O(n) 求第k远点的方法,简直虐哭…

我们要求的内容是关于一个点的左边的一个端点与右边的一个端点(并且这两个端点之间的距离是确定的),于是我们就在单调性上研究研究。可以发现的是,这两个端点只会向右延伸,于是直接向右边一直移动指针过去就可以了。

int L=1,R=k+1;
    d[0][1]=k+1;
    for(int i=2;i<=n;i++){
        while(R<n&&L<i&&A[R+1]-A[i]<A[i]-A[L])R++,L++;
        d[0][i]=(A[i]-A[L]>=A[R]-A[i]?L:R);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值