[BZOJ2006][NOI2010]超级钢琴
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,表示按编号从小到大每个音符的美妙度。
N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲
Output
只有一个整数,表示乐曲美妙度的最大值。
Sample Input
4 3 2 3
3
2
-6
8
3
2
-6
8
Sample Output
11
【样例说明】
共有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。
【样例说明】
共有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。
贪心地想,我们一定是要取最大的$K$个和弦(废话)。
首先做个前缀和肯定没问题吧,把求区间和复杂度降为$O(1)$。
一个区间为$sum[r] - sum[l - 1]$,我们固定$l$,那么要求$sum[r]$越大越好。
然后我们对于每一个点$i$,取出$[i+L-1, i+R-1]$中的前缀和中的最大值,算出这个区间的值,插入堆里。
然后每次取堆顶,一定是全局的最优,然后假设我们取出的是位置$i$的答案,我们取出了这个位置的最大值,但是这个位置的次大值仍然有可能成为答案,所以我们把它的次大值再插入堆里,如果这次取出的是最小值那么就不进行任何操作。
对于上面快速取出区间中第K大的值想都不想就上主席树(其实别的我都不会)。
对了前缀和有负数而且太大要离散化一下。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <cmath> #include <map> using namespace std; #define reg register #define gc getchar inline int read() { int res=0;char ch=gc();bool fu=0; while(!isdigit(ch))fu|=(ch=='-'), ch=gc(); while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc(); return fu?-res:res; } #define N 500005 int n, k, L, R; int a[N], sum[N]; #define pii pair<int, int> #define mkp make_pair priority_queue<pii> q; int tim[N]; int cpy[N], u; namespace seg { int tr[N*25], ls[N*25], rs[N*25], tot, root[N]; int Insert(int o, int l, int r, int p) { int jd = ++tot; tr[jd] = tr[o] + 1; if (l == r) return jd; ls[jd] = ls[o], rs[jd] = rs[o]; int mid = (l + r) >> 1; if (p <= mid) ls[jd] = Insert(ls[o], l, mid, p); else rs[jd] = Insert(rs[o], mid + 1, r, p); return jd; } int K_th(int l, int r, int o1, int o2, int k) { if (l == r) return l; int sum = tr[ls[o2]] - tr[ls[o1]]; int mid = (l + r) >> 1; if (sum >= k) return K_th(l, mid, ls[o1], ls[o2], k); else return K_th(mid + 1, r, rs[o1], rs[o2], k - sum); } } int now = 1; long long ans; int lmx[N], rmx[N]; int main() { n = read(), k = read(), L = read(), R = read(); for (reg int i = 1 ; i <= n ; i ++) a[i] = read(); for (reg int i = 1 ; i <= n ; i ++) cpy[i] = sum[i] = sum[i - 1] + a[i]; sort(cpy + 1, cpy + 1 + n); u = unique(cpy + 1, cpy + 1 + n) - cpy - 1; for (reg int i = 1 ; i <= n ; i ++) sum[i] = lower_bound(cpy + 1, cpy + 1 + u, sum[i]) - cpy; for (reg int i = 1 ; i <= n ; i ++) seg::root[i] = seg::Insert(seg::root[i - 1], 1, u, sum[i]); for (reg int i = 1 ; i <= n - L + 1 ; i ++) { int l = min(n, i + L - 1), r = min(n, i + R - 1); lmx[i] = l, rmx[i] = r; tim[i] = 1; // printf("%d %d %d\n", i, l, r); q.push(mkp(cpy[seg::K_th(1, u, seg::root[l - 1], seg::root[r], rmx[i] - lmx[i] + 2 - tim[i])] - cpy[sum[i - 1]], i)); } // for (int i = 1; i <= n ; i ++) printf("%d ", sum[i]);puts(""); for ( ; now <= k ; now ++) { pii tp = q.top();q.pop(); ans += tp.first; tim[tp.second] ++; // printf("%d %d\n", tp.second, tp.first); if (tim[tp.second] > rmx[tp.second] - lmx[tp.second] + 1) continue; int l = min(n, tp.second + L - 1), r = min(n, tp.second + R - 1); q.push(mkp(cpy[seg::K_th(1, u, seg::root[l - 1], seg::root[r], rmx[tp.second] - lmx[tp.second] + 2 - tim[tp.second])] - cpy[sum[tp.second - 1]], tp.second)); } cout << ans << endl; return 0; }