【JZOJ5409】Fantasy

来源:互联网 发布:金融电子化公司 知乎 编辑:程序博客网 时间:2024/06/08 06:33

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无关的算法,跑得真慢。

#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 longusing 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);}
原创粉丝点击