【NOI2010T2】超级钢琴-主席树+优先队列

测试地址:超级钢琴
做法:题目要求的是,给定数列中长度在LR之间的连续子段中,前K大的子段和之和。乍一看十分复杂,我们可以慢慢分析。
实际上我们可以一个一个地提取出前K大的子段,怎么提取呢?先考虑提取最大的子段,我们可以求对于每一个i,以第i个元素结尾的最大的满足要求的子段和,这个怎么求呢?对数列求一个前缀和,令sum(i)=a(1)+...+a(i),那么要求以第i个元素结尾的最大的满足要求的子段和,实际上就是求sum(i)sum(iL),sum(i)sum(iL1),...,sum(i)sum(iR)中的最大值,这个最大值等于sum(i)减去sum(iL),sum(iL1),...,sum(iR)中的最小值,求区间最小值显然可以用线段树维护。这样,我们对于每一个i求出这样的值,然后里面最大的就是整个数列中满足要求的最大子段和。
取出了最大子段和后,要求次大子段和了,这回要怎么办呢?其实我们可以在求最大子段和的时候排除掉已经取掉的子段,假设这个子段以a(i)结尾,那么去掉这个子段后,以a(i)结尾的最大子段和是什么呢?那就是原来以a(i)结尾的次大子段和。也就是说,在原来那一堆值里,我们去掉了一个值,又要添加以a(i)结尾的次大子段和进那一堆值,然后再求最大值取出。扩展到取第K段,我们取第K1段之后,我们紧接着就要求以这一段结尾元素结尾的下一个最大的段,因此我们必须解决求区间内第任意小值的问题,然后将它的值插入到备选的位置中,满足插入、查询最小值和删除最小值的数据结构显然就是堆了,也可以用STL中的优先队列来写。那么我们只需要解决求区间第任意小的问题了,这个显然可以用主席树处理,这个问题就解决了,总的时间复杂度大约是O((N+K)logN)
注意在使用主席树的时候,要开够空间,不然会RE或WA(只是显示,实质上还是RE)。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define ll long long
using namespace std;
int n,K,L,R,tot=0,a[500010],pos[500010],rt[500010],t[500010];
ll sum[500010];
int seg[20000010],lft[20000010],rht[20000010];
struct forsort {int val,id;} f[500010];
struct state
{
    ll val;
    int id;
    bool operator < (state a) const
    {
        return val<a.val;
    }
};
priority_queue <state> Q;

bool cmp(forsort a,forsort b)
{
    return a.val<b.val;
}

void buildtree(int &no,int l,int r)
{
    no=++tot;
    seg[no]=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    buildtree(lft[no],l,mid);
    buildtree(rht[no],mid+1,r);
}

void add(int &no,int last,int l,int r,int x)
{
    no=++tot;
    seg[no]=seg[last],lft[no]=lft[last],rht[no]=rht[last];
    if (l==r)
    {
        seg[no]++;
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) add(lft[no],lft[last],l,mid,x);
    else add(rht[no],rht[last],mid+1,r,x);
    seg[no]=seg[lft[no]]+seg[rht[no]];
}

int query(int l,int r,int k)
{
    if (k>seg[rt[r]]-seg[rt[l-1]]) return -1;
    int last=rt[l-1],no=rt[r],x=1,y=n+1;
    while(x!=y)
    {
        int mid=(x+y)>>1;
        if (seg[lft[no]]-seg[lft[last]]>=k) y=mid,no=lft[no],last=lft[last];
        else x=mid+1,k-=seg[lft[no]]-seg[lft[last]],no=rht[no],last=rht[last];
    }
    return f[x].id-1;
}

void work1()
{
    scanf("%d%d%d%d",&n,&K,&L,&R);
    f[1].id=1,f[1].val=0;
    sum[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        f[i+1].id=i+1,f[i+1].val=f[i].val+a[i];
        sum[i]=(ll)f[i+1].val;
    }
    sort(f+1,f+n+2,cmp);
    for(int i=1;i<=n+1;i++)
    {
        pos[f[i].id]=i;
    }
    buildtree(rt[0],1,n+1);
    for(int i=1;i<=n+1;i++) add(rt[i],rt[i-1],1,n+1,pos[i]);
}

void work2()
{
    state next;
    for(int i=L;i<=n;i++)
    {
        t[i]=1;
        int p=query(max(i-R+1,1),i-L+1,t[i]);
        next.val=sum[i]-sum[p],next.id=i;
        Q.push(next);
    }
    ll ans=0;
    while(K--)
    {
        state now=Q.top();Q.pop();
        ans+=now.val;
        t[now.id]++;
        int p=query(max(now.id-R+1,1),now.id-L+1,t[now.id]);
        if (p!=-1)
        {
            next.val=sum[now.id]-sum[p],next.id=now.id;
            Q.push(next);
        }
    }
    printf("%lld",ans);
}

int main()
{
    work1();
    work2();

    return 0;
}
posted @ 2017-09-11 21:58  Maxwei_wzj  阅读(89)  评论(0编辑  收藏  举报