BZOJ 2006 超级钢琴(划分树+优先队列)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2006

题意: 给出一个数列A,L,R,构造出一个新的集合,集合中的数字为A中任意连续t(L<=t<=R)个数字的和(集合中的数字可以重复)。求集合中前K大的数字和。

思路:首先,我们令S[i]表示A的前i项和,P[i] 表示以A中第i个数字结尾可以取到的最大值,那么显然有:

求出P之后,我们可以用一个堆或者优先队列来维护最大值。那么现在来了另外一个问题,某次在优先队列中取得的是P[i],那么显然要将其从优先队列中删除,此时,以i结尾的次大值也是有可能成为最后答案中一部分的,类似的,我们可能还要求第三大、第四大,也就是求一段区间的第三小、第四小等,我们可以用划分树解决这个问题。现在,整个问题都解决了,首先,求出S,将S建立划分树。然后将所有P插入优先队列。每次从优先队列中取出最大值,删掉,并将这个最大值对应的原数列中位置结尾的下一大的值插入优先队列(这一步也就是在划分树中找到下一小的值),直到取出K个。



int n,m,L,R;
i64 b[N],s[N],S[N];


struct node
{
    int L,R;
};


node a[N<<2];
i64 t[35][N];
int tot[35][N];


void init()
{
    RD(n,m); RD(L,R);
    int i;
    for(i=2;i<=n+1;i++) RD(b[i]),S[i]=t[1][i]=s[i]=s[i-1]+b[i];
    n++;
    sort(s+1,s+n+1);
}


void build(int dep,int u,int L,int R)
{
    a[u].L=L;
    a[u].R=R;
    if(L==R) return;
    int mid=(L+R)>>1;
    int i,sameNum=mid-L+1;
    FOR(i,L,R) if(t[dep][i]<s[mid]) sameNum--;
    int LL=L,LR=mid,RL=mid+1,RR=R;
    int Lnum=0,Rnum=0;
    i64 x;
    FOR(i,L,R)
    {
        if(i==L) tot[dep][i]=0;
        else tot[dep][i]=tot[dep][i-1];
        x=t[dep][i];
        if(x<s[mid])
        {
            tot[dep][i]++;
            t[dep+1][LL+Lnum]=x;
            Lnum++;
        }
        else if(x>s[mid])
        {
            t[dep+1][RL+Rnum]=x;
            Rnum++;
        }
        else
        {
            if(sameNum>0)
            {
                sameNum--;
                tot[dep][i]++;
                t[dep+1][LL+Lnum]=x;
                Lnum++;
            }
            else
            {
                t[dep+1][RL+Rnum]=x;
                Rnum++;
            }
        }
    }
    build(dep+1,u*2,LL,LR);
    build(dep+1,u*2+1,RL,RR);
}


i64 query(int dep,int u,int L,int R,int K)
{
    if(L==R) return t[dep][L];
    int x,y,xx,yy,_L,_R;
    int mid=(a[u].L+a[u].R)>>1;
    if(L==a[u].L) x=0;
    else x=tot[dep][L-1];
    y=tot[dep][R]-x;
    if(y>=K)
    {
        _L=a[u].L+x;
        _R=a[u].L+x+y-1;
        return query(dep+1,u*2,_L,_R,K);
    }
    else
    {
        xx=L-a[u].L-x;
        yy=R-L+1-y;
        _L=mid+1+xx;
        _R=mid+1+xx+yy-1;
        return query(dep+1,u*2+1,_L,_R,K-y);
    }
}


struct Node
{
    int pos,k;
    i64 x;


    Node(){}
    Node(int _pos,int _k,i64 _x)
    {
        pos=_pos;
        k=_k;
        x=_x;
    }
    
    int operator<(const Node &a) const
    {
        return x<a.x;
    }
};




priority_queue<Node> Q;


int main()
{
    init(); build(1,1,1,n);
    int i,x,y;
    for(i=L+1;i<=n;i++)
    {
        x=max(1,i-R);
        y=i-L;
        Q.push(Node(i,1,S[i]-query(1,1,x,y,1)));
    }
    i64 ans=0;
    Node p;
    while(m--)
    {
        p=Q.top(); Q.pop();
        ans+=p.x;
        x=max(1,p.pos-R);
        y=p.pos-L;
        if(p.k+1>y-x+1) continue;
        Q.push(Node(p.pos,p.k+1,S[p.pos]-query(1,1,x,y,p.k+1)));
    }
    PR(ans);
}

你可能感兴趣的:(优先队列)