BZOJ4367 IOI2014holiday假期(整体二分+主席树)

  显然最优策略是先走到一边要到达的最远城市,再换方向走到另一边要到达的最远城市(当然也可以直接停止),路上参观景点。

  先仅考虑求出只向左走,花费时间i时的最优解。如果能求出这个,类似的就可以求出所有情况。

  显然时间越长,应该往左边走的越远,参观的越多,但是这个最远城市的变化不一定连续,没法愉快地双指针或者直接二分答案。

  考虑类似整体二分的做法。设当前要求i在l~r,最远城市在x~y之间的最优解,对i=mid暴力求出最优解,然后递归处理两边。暴力需要进行nlog次,每次暴力需要求区间k大和,可以用主席树做到log,于是复杂度O(nlog2n)。

  注意起点处只能取一次。并且对mid求出的最远点如果有多解考虑清楚选哪一个。

  upd:才想起来这玩意就是决策单调性

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cassert>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 100010
#define ll long long
int n,start,m,a[N],b[N],root[N],cnt,t;
ll fl[N<<2],gl[N<<2],fr[N<<2],gr[N<<2];
struct data{int l,r,x;ll sum;
}tree[N*25];
struct data2{ll x;int d;};
void ins(int &k,int l,int r,int x) 
{
    tree[++cnt]=tree[k],k=cnt;
    tree[k].x++,tree[k].sum+=b[x];
    if (l==r) return;
    int mid=l+r>>1;
    if (x<=mid) ins(tree[k].l,l,mid,x);
    else ins(tree[k].r,mid+1,r,x);
}
ll query(int x,int y,int l,int r,int k)
{
    if (!y) return 0;
    if (l==r) return 1ll*min(tree[y].x-tree[x].x,k)*b[l];
    int mid=l+r>>1,t=tree[tree[y].r].x-tree[tree[x].r].x;
    if (t>=k) return query(tree[x].r,tree[y].r,mid+1,r,k);
    else return tree[tree[y].r].sum-tree[tree[x].r].sum+query(tree[x].l,tree[y].l,l,mid,k-t);
}
data2 calc(int tot,int x,int y,int op,int isr)
{
    int mx=isr?x:y;ll ans=0;
    if (isr)
    {
        for (int i=x;i<=y;i++)
        if (tot>=abs(start-i)*(1+op))
        {
            ll v;
            if (i<start) v=query(root[i-1],root[start-op],1,t,tot-(start-i)*(1+op));
            else v=query(root[start-1+op],root[i],1,t,tot-(i-start)*(1+op));
            if (v>ans) ans=v,mx=i;
        }
    }
    else
    {
        for (int i=y;i>=x;i--)
        if (tot>=abs(start-i)*(1+op))
        {
            ll v;
            if (i<start) v=query(root[i-1],root[start-op],1,t,tot-(start-i)*(1+op));
            else v=query(root[start-1+op],root[i],1,t,tot-(i-start)*(1+op));
            if (v>ans) ans=v,mx=i;
        }
    }
    return (data2){ans,mx};
}
void solve1(int l,int r,int x,int y)
{
    if (l>r||x>y) return;
    int mid=l+r>>1;data2 t=calc(mid,x,y,0,0);
    fl[mid]=t.x;
    solve1(l,mid-1,t.d,y);
    solve1(mid+1,r,x,t.d);
}
void solve2(int l,int r,int x,int y)
{
    if (l>r||x>y) return;
    int mid=l+r>>1;data2 t=calc(mid,x,y,0,1);
    fr[mid]=t.x;
    solve2(l,mid-1,x,t.d);
    solve2(mid+1,r,t.d,y);
}
void solve3(int l,int r,int x,int y)
{
    if (l>r||x>y) return;
    int mid=l+r>>1;data2 t=calc(mid,x,y,1,0);
    gl[mid]=t.x;
    solve3(l,mid-1,t.d,y);
    solve3(mid+1,r,x,t.d);
}
void solve4(int l,int r,int x,int y)
{
    if (l>r||x>y) return;
    int mid=l+r>>1;data2 t=calc(mid,x,y,1,1);
    gr[mid]=t.x;
    solve4(l,mid-1,x,t.d);
    solve4(mid+1,r,t.d,y);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4367.in","r",stdin);
    freopen("bzoj4367.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),start=read()+1,m=read();
    for (int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(b+1,b+n+1);
    t=unique(b+1,b+n+1)-b-1;
    for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t+1,a[i])-b;
    for (int i=1;i<=n;i++) 
    {
        root[i]=root[i-1];
        ins(root[i],1,t,a[i]);
    }
    solve1(0,m,1,start);
    solve2(0,m,start,n);
    solve3(0,m,1,start-1);
    solve4(0,m,start+1,n);
    ll ans=0;
    for (int i=0;i<=m;i++)
    ans=max(ans,gl[i]+fr[m-i]),
    ans=max(ans,gr[i]+fl[m-i]);
    cout<<ans;
    return 0;
}

 

posted @ 2018-10-28 17:02  Gloid  阅读(213)  评论(0编辑  收藏  举报