[IOI2014]holiday 假期 题解

[IOI2014]holiday 假期 题解

Problem

​ 给定一个序列,序列上每一个点有点权,给定一个出发点和一个花费,每次可以用1的花费在相邻两点间移动,也可以用1的花费获取当前点的点权的价值,每个点只可以被获取一次,求最大价值。

Solution

​ 很容易发现,最优方法一定是先一直朝一个方向走,之后再看是否回头朝另一个方向走。

​ 那么我们可以把这个问题分成四个小问题,我们设\(f_{1,i},f_{2.i},f_{3,i},f_{4,i}\)分别表示花费为\(i\),向左/向右/向左后回到起点/向右后回到起点的最大价值,由它们组合得到答案。

\(O(n^2logn)\)的DP是不难想到的,只要枚举最远到达的点,之后再用除去走完一遍路程的剩余的花费\(k\)在这段路程中选最大的\(k\)个价值即可,可以用主席树维护。

​ 考虑如何去优化这个DP,我们可以发现,每当有更多的可支配花费,最优的最远到达点只增不减,具有决策单调性,这个性质也比较明显。

​ 接下来,考虑如何实现,我们可以用决策单调性一个很常见的办法:分治来实现优化。具体而言,类似于整体二分一样。

​ 这道题细节很多,我写了一天,心态数次炸裂,最终终于完成,下面说说几个坑点:

​ 1.单调栈不好写这道题!我一开始写的是单调栈,最终感觉不好写,只有50pts,改换了分治。

​ 2.主席树注意判一下查询区间是否为空。

​ 3.\(m\)的范围不是1e5......

​ 4.最恶心的一点,注意起点的权值不要算到两次。

Code

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,m,k;
int d[100005],t[100005],pos[300005],root[100005];
LL Ans,f1[300005],f2[300005],f3[300005],f4[300005];//L,R,L->R,R->L

struct Segment_Tree{

    int tot;

    LL sum[2000005];

    int seg[2000005],son[2000005][2];

    void insert(int &k,int p,int l,int r,int pos){
        k=++tot;
        seg[k]=seg[p]+1;
        son[k][0]=son[p][0];
        son[k][1]=son[p][1];
        sum[k]=sum[p]+t[pos];
        if(l==r)
            return;
        int mid=l+r>>1;
        if(pos<=mid)
            insert(son[k][0],son[p][0],l,mid+0,pos);
        else
            insert(son[k][1],son[p][1],mid+1,r,pos);
        return;
    }

    LL query(int k,int p,int l,int r,int kth){
        if(l==r)
            return 1LL*kth*t[l];
        LL res=0;
        int mid=l+r>>1;
        if(seg[son[k][1]]-seg[son[p][1]]>=kth)
            res+=query(son[k][1],son[p][1],mid+1,r,kth);
        else{
            res+=sum[son[k][1]]-sum[son[p][1]];
            res+=query(son[k][0],son[p][0],l,mid+0,kth-(seg[son[k][1]]-seg[son[p][1]]));
        }
        return res;
    }

    LL Query(int l,int r,int k){
        return l>r?0:query(root[r],root[l-1],1,n,min(k,r-l+1));
    }

}T;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
       if(ch=='-')f=-1;
       ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
       x=(x<<1)+(x<<3)+ch-'0';
       ch=getchar();
    }
    return x*f;
}

void solve1(int l,int r,int L,int R){
    if(l>r)
        return;
    int mid=l+r>>1;
    for(register int i=L;i<=R;++i){
        if(mid-(k-i<<0)<0)
            continue;
        LL res=T.Query(i,k,mid-(k-i<<0));
        if(!pos[mid]||res>f1[mid]){
            pos[mid]=i;
            f1[mid]=res;
        }
    }
    solve1(l,mid-1,pos[mid],R);
    solve1(mid+1,r,L,pos[mid]);
    return;
}

void solve2(int l,int r,int L,int R){
    if(l>r)
        return;
    int mid=l+r>>1;
    for(register int i=L;i<=R;++i){
        if(mid-(i-k<<0)<0)
            continue;
        LL res=T.Query(k,i,mid-(i-k<<0));
        if(!pos[mid]||res>f2[mid]){
            pos[mid]=i;
            f2[mid]=res;
        }
    }
    solve2(l,mid-1,L,pos[mid]);
    solve2(mid+1,r,pos[mid],R);
    return;
}

void solve3(int l,int r,int L,int R){
    if(l>r)
        return;
    int mid=l+r>>1;
    for(register int i=L;i<=R;++i){
        if(mid-(k-i<<1)<0)
            continue;
        LL res=T.Query(i,k-1,mid-(k-i<<1));
        if(!pos[mid]||res>f3[mid]){
            pos[mid]=i;
            f3[mid]=res;
        }
    }
    solve3(l,mid-1,pos[mid],R);
    solve3(mid+1,r,L,pos[mid]);
    return;
}

void solve4(int l,int r,int L,int R){
    if(l>r)
        return;
    int mid=l+r>>1;
    for(register int i=L;i<=R;++i){
        if(mid-(i-k<<1)<0)
            continue;
        LL res=T.Query(k+1,i,mid-(i-k<<1));
        if(!pos[mid]||res>f4[mid]){
            pos[mid]=i;
            f4[mid]=res;
        }
    }
    solve4(l,mid-1,L,pos[mid]);
    solve4(mid+1,r,pos[mid],R);
    return;
}

int main(){
    
    n=read();k=read();m=read();++k;

    for(register int i=1;i<=n;++i)
        d[i]=t[i]=read();

    sort(t+1,t+n+1);

    for(register int i=1;i<=n;++i){
        d[i]=lower_bound(t+1,t+n+1,d[i])-t;
        T.insert(root[i],root[i-1],1,n,d[i]);
    }

    memset(pos,0,sizeof pos);solve1(1,m,max(1,k-(m>>0)),k);

    memset(pos,0,sizeof pos);solve2(1,m,k,min(n,k+(m>>0)));

    memset(pos,0,sizeof pos);solve3(1,m,max(1,k-(m>>1)),k);

    memset(pos,0,sizeof pos);solve4(1,m,k,min(n,k+(m>>1)));

    for(register int i=0;i<=m;++i)
        Ans=max(Ans,max(f1[i]+f4[m-i],f2[i]+f3[m-i]));

    printf("%lld\n",Ans);

    return 0;
}
posted @ 2020-09-22 20:47  zjy123456  阅读(217)  评论(0编辑  收藏  举报