首页 > 代码库 > BZOJ 2006 超级钢琴(堆+主席树)

BZOJ 2006 超级钢琴(堆+主席树)

很好的一道题。

题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少。(n,k<=5e5).

首先将区间和转化为前缀和之差,那么我们想要区间和的总和最大,一个朴素的想法是把所有满足限制的区间和排序,取最大的k个。

考虑每个右端点i,其中有效的左端点是[i-R+1,i-L+1].如果我们对于每个右端点都找到“当前”最大的区间和,那么把它们扔进大根堆里维护一下即可。

由于sum[i]一定,那么只要左端点的sum值越小越好,于是我们每次从大根堆里弹出一个数,再从弹出的这个值的右端点出发,找一个次大的值。

这样从大根堆里面弹出k次就是最大的k个区间和了。

那么我们需要维护一个数据结构支持查询静态区间第k小,显然主席树可以满足这个要求。

 

技术分享
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <bitset>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
const int N=500005;
//Code begin...

int a[N], sum[N], vis[N];
struct qnode{
    int id, val;
    qnode(int _id=0, int _val=0):id(_id),val(_val){}
    bool operator<(const qnode &r)const{return val<r.val;}
};
priority_queue<qnode>que;
VI v;
int root[N], s[N*80], ls[N*80], rs[N*80], sz;

void insert(int l, int r, int x, int &y, int val){
    y=++sz; s[y]=s[x]+1;
    if (l==r) return ;
    ls[y]=ls[x]; rs[y]=rs[x];
    int mid=(l+r)>>1;
    if (val<=mid) insert(l,mid,ls[x],ls[y],val);
    else insert(mid+1,r,rs[x],rs[y],val);
}
int query(int l, int r, int x, int y, int L)
{
    if (l==r) return l;
    int mid=(l+r)>>1, val=s[ls[y]]-s[ls[x]];
    if (val>=L) return query(l,mid,ls[x],ls[y],L);
    return query(mid+1,r,rs[x],rs[y],L-val);
}
int main ()
{
    int n, k, L, R;
    qnode tmp;
    scanf("%d%d%d%d",&n,&k,&L,&R);
    FOR(i,1,n) scanf("%d",a+i), sum[i]=sum[i-1]+a[i], v.pb(sum[i]);
    v.pb(0);
    sort(v.begin(),v.end());
    int siz=unique(v.begin(),v.end())-v.begin();
    FOR(i,1,n+1) {
        int x=lower_bound(v.begin(),v.begin()+siz,sum[i-1])-v.begin()+1;
        insert(1,siz,root[i-1],root[i],x);
    }
    FOR(i,1,n) {
        int l=max(i-R,0), r=i-L;
        if (r<l) continue;
        int x=query(1,siz,root[l],root[r+1],1); que.push(qnode(i,sum[i]-v[x-1]));
    }
    int num=0;
    LL ans=0;
    while (num<k) {
        tmp=que.top(); que.pop(); ans+=tmp.val; ++vis[tmp.id]; ++num;
        int l=max(tmp.id-R,0), r=tmp.id-L;
        if (r-l<vis[tmp.id]) continue;
        int x=query(1,siz,root[l],root[r+1],vis[tmp.id]+1); que.push(qnode(tmp.id,sum[tmp.id]-v[x-1]));
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

BZOJ 2006 超级钢琴(堆+主席树)