bzoj2006 noi2010 超级钢琴 主席树 + 优先队列
Time Limit: 20 Sec Memory Limit: 552 MB
Submit: 2435 Solved: 1195
Description
小 Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和 弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。 小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有 超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。
Input
第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所包含音符个数的下限和上限。 接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。
Output
只有一个整数,表示乐曲美妙度的最大值。
Sample Input
3
2
-6
8
Sample Output
【样例说明】
共有5种不同的超级和弦:
音符1 ~ 2,美妙度为3 + 2 = 5
音符2 ~ 3,美妙度为2 + (-6) = -4
音符3 ~ 4,美妙度为(-6) + 8 = 2
音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
音符2 ~ 4,美妙度为2 + (-6) + 8 = 4
最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。
HINT
那么想要这个子序列的权值和尽量大,那么就要前面的那个前缀和尽可能小。如果数目不够,就第2小。再不够,就第3小。
于是我们维护一个优先队列,维护3元组(v,r,k),v表示这个序列的权值和,r表示这个序列的结尾位置,k表示v是r结尾的符合条件的序列的第k大权值和
以每个位置为结尾的最大子序列权值和,每次取出堆顶时再放进堆中一个结尾位置相同的第k+1大的权值和。
这样k次就能出解。
这要求我们能够快速求出区间第k小,利用可持久化线段树即可。
时间复杂度O(klogn).
代码:
//File Name: bzoj2006.cpp //Author: long //Mail: 736726758@qq.com //Created Time: 2016年07月31日 星期日 14时12分42秒 #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #define LL long long #define hash _hash_ using namespace std; const int MAXN = 500000 + 3; int h_num[MAXN],hash[MAXN],tot,top; void hash_init(){ sort(h_num+1,h_num+top+1); tot = 0; hash[++tot] = h_num[1]; for(int i=2;i<=top;i++){ if(h_num[i] != h_num[i-1]) hash[++tot] = h_num[i]; } } int hash_find(int x){ int l = 1,r = tot,mid; while(l <= r){ mid = (l + r) >> 1; if(hash[mid] < x) l = mid + 1; else r = mid - 1; } return l; } struct T{ int v,r,k; bool operator<(const T&a) const{ return v < a.v; } }; priority_queue<T> que; struct Node{ int ls,rs,w; Node(){ls = rs = w = 0;} }node[MAXN * 20]; int a[MAXN],sum[MAXN],root[MAXN],sz,N,K,L,R; void update(int &i,int l,int r,int w){ node[++sz] = node[i]; i = sz; node[i].w++; if(l == r) return ; int mid = (l + r) >> 1; if(w <= mid) update(node[i].ls,l,mid,w); else update(node[i].rs,mid+1,r,w); } int query(int i,int j,int l,int r,int k){ if(l == r) return l; int t = node[node[j].ls].w - node[node[i].ls].w; int mid = (l + r) >> 1; if(t >= k) return query(node[i].ls,node[j].ls,l,mid,k); else return query(node[i].rs,node[j].rs,mid+1,r,k-t); } void get(int i,int &l,int &r){ l = i - R, r = i - L; if(l < 0) l = 0; if(r < 0) r = -1; } LL solve(){ sum[0] = 0; top = 0; h_num[++top] = 0; for(int i=1;i<=N;i++){ sum[i] = sum[i-1] + a[i]; h_num[++top] = sum[i]; } hash_init(); for(int i=0;i<=N;i++) sum[i] = hash_find(sum[i]); sz = 0; root[1] = root[0] = 0; update(root[1],1,tot,sum[0]); for(int i=1;i<=N;i++){ root[i+1] = root[i]; update(root[i+1],1,tot,sum[i]); } while(!que.empty()) que.pop(); LL ans = 0; int now,l,r; for(int i=L;i<=N;i++){ get(i,l,r); if(r < 0) continue; //printf("i = %d l = %d r = %d\n",i,l,r); now = hash[sum[i]] - hash[query(root[l],root[r+1],1,tot,1)]; //printf("q = %d now = %d\n",query(root[l],root[r+1],1,tot,1),now); que.push((T){now,i,1}); } T u; while(true){ u = que.top(); que.pop(); //printf("u.v = %d\n",u.v); ans += u.v; K--; if(!K) break; get(u.r,l,r); if(r - l + 1 <= u.k) continue; now = hash[sum[u.r]] - hash[query(root[l],root[r+1],1,tot,u.k+1)]; que.push((T){now,u.r,u.k+1}); } return ans; } int main(){ while(~scanf("%d %d %d %d",&N,&K,&L,&R)){ for(int i=1;i<=N;i++) scanf("%d",&a[i]); //cout << solve() << endl; printf("%lld\n",solve()); } return 0; }