BZOJ 2006 超级钢琴(堆+主席树)
很好的一道题。
题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少。(n,k<=5e5).
首先将区间和转化为前缀和之差,那么我们想要区间和的总和最大,一个朴素的想法是把所有满足限制的区间和排序,取最大的k个。
考虑每个右端点i,其中有效的左端点是[i-R+1,i-L+1].如果我们对于每个右端点都找到“当前”最大的区间和,那么把它们扔进大根堆里维护一下即可。
由于sum[i]一定,那么只要左端点的sum值越小越好,于是我们每次从大根堆里弹出一个数,再从弹出的这个值的右端点出发,找一个次大的值。
这样从大根堆里面弹出k次就是最大的k个区间和了。
那么我们需要维护一个数据结构支持查询静态区间第k小,显然主席树可以满足这个要求。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 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; }