【JZOJ5409】Fantasy

Description

Y sera 陷入了沉睡,幻境中它梦到一个长度为N 的序列{Ai}。
对于这个序列的每一个子串,定义其幻境值为这个子串的和,现在Y sera 希望选择K 个不同的子串并使得这K 个子串的幻境值之和最大。
然而由于梦境中的种种限制,这些子串的长度必须在L 到R 之间。
你需要告诉她,最大的幻境值之和。

Solution

先对 Ai 做前缀和 Si ,然后对于一个 i 作为结尾,一个合法的j满足 lijr ,(即 [j+1,i] 这个区间),于是找到 j (j[il,ir])使得 SjSi 最大,然后这个区间扔进堆里。做完一遍后,每次从堆中取出权值最大的区间,对于左端点再向左向右找到一个最大的左端点匹配当前的右端点(也就是两个新区间,权值比这个区间小且是剩下的最大的),扔进堆里继续维护即可。时间复杂度 O(Klog2N)

还有一种与 K 无关的算法,同样考虑前缀和,首先二分一个mid表示组成答案的区间中权值最小的,于是我们可以用线段树统计出大于等于 mid 的区间个数,与 K 比较以缩小上下界,复杂度就是O(Nlog22N)的。

Solution

K <script type="math/tex" id="MathJax-Element-2118">K</script>无关的算法,跑得真慢。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 100010
#define _ 1000000000
#define ll long long
using namespace std;
struct node{
    ll s;
    int x,l,r;
}tr[N*55];
ll s[N];
void update(int v){
    tr[v].s=tr[tr[v].l].s+tr[tr[v].r].s;
    tr[v].x=tr[tr[v].l].x+tr[tr[v].r].x;
}
int rt[N],tot=0;
void insert(int &v,int l,int r,int x){
    tr[++tot]=tr[v],v=tot;
    if(l==r){
        tr[v].s+=x,tr[v].x++;
        return;
    }
    int mid=((ll)l+r)/2;
    if(x<=mid) insert(tr[v].l,l,mid,x);
    else insert(tr[v].r,mid+1,r,x);
    update(v);
}
ll findsum(int v0,int v1,int l,int r,int x,int y){
    if(l==x && r==y){
        return tr[v0].s-tr[v1].s;
    }
    int mid=((ll)l+r)/2;
    if(y<=mid) return findsum(tr[v0].l,tr[v1].l,l,mid,x,y);
    else if(x>mid) return findsum(tr[v0].r,tr[v1].r,mid+1,r,x,y);
    else return findsum(tr[v0].l,tr[v1].l,l,mid,x,mid)+findsum(tr[v0].r,tr[v1].r,mid+1,r,mid+1,y);
}
int find(int v0,int v1,int l,int r,int x,int y){
    if(l==x && r==y){
        return tr[v0].x-tr[v1].x;
    }
    int mid=((ll)l+r)/2;
    if(y<=mid) return find(tr[v0].l,tr[v1].l,l,mid,x,y);
    else if(x>mid) return find(tr[v0].r,tr[v1].r,mid+1,r,x,y);
    else return find(tr[v0].l,tr[v1].l,l,mid,x,mid)+find(tr[v0].r,tr[v1].r,mid+1,r,mid+1,y);
}
int n,K,L,R;
bool check(int x){
    int cn=0;
    fo(i,1,n-L+1)
    {
        int l=i+L-2,r=min(i+R-1,n);
        if((ll)s[i-1]+x<=_*2) cn+=find(rt[r],rt[l],0,_*2,s[i-1]+x,_*2);
    }
    return cn>=K;
}
int main()
{
    freopen("fantasy.in","r",stdin);
    freopen("fantasy.out","w",stdout);
    scanf("%d %d %d %d",&n,&K,&L,&R);
    fo(i,1,n)
    {
        int x;
        scanf("%d",&x);
        s[i]=s[i-1]+x;
        rt[i]=rt[i-1],insert(rt[i],0,_*2,s[i]+_);
    }
    int l=0,r=_*2;
    while(l+1<r){
        int mid=((ll)l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    if(check(r)) l=r;
    int cn=0;
    ll ans=0;
    fo(i,1,n-L+1)
    {
        int p=i+L-2,q=min(i+R-1,n);
        int tmp=find(rt[q],rt[p],0,_*2,s[i-1]+l,_*2);
        cn+=tmp;
        ans+=findsum(rt[q],rt[p],0,_*2,s[i-1]+l,_*2)-(s[i-1]+_)*tmp;
    }
    if(cn>K) ans=ans-(cn-K)*(l-_);
    printf("%lld",ans);
}
posted @ 2017-10-21 20:03  sadstone  阅读(43)  评论(0编辑  收藏  举报