題目node
在一個長度爲 \(n\) 的數列裏,找出長度在 \([L,R]\) 中的 \(k\) 個子段,使它們的和最大。spa
咱們能夠把問題轉化爲求出一部分的:code
其中 \(sum\) 是前綴和。get
能夠考慮枚舉左端點,而後用 ST 表找到最優的右端點,而後把它丟到一個大根堆裏,就能夠解決答案了嗎?it
並不。緣由是,一個左端點,除了最優的右端點,其它的可能對於總答案來講仍是較爲優秀的,也不能忽略。而這一個問題能夠分治解決:假設當前最優的右端點是 \(x\),能夠在 \([l,x-1]\) 或者 \([x+1,r]\) 裏再找一個。這是整道題的核心。io
多是我本身的鍋,可是實踐發現枚舉左端點要比枚舉右端點更優秀。class
const int N = 5e5 + 10; int n, k, L, R; ll sum[N], f[N][25], ans; struct node { ll x, y, l, r; inline bool operator < (const node& a) const { return sum[y] - sum[x - 1] < sum[a.y] - sum[a.x - 1]; } }; priority_queue<node> q; ll query (int l, int r) { int t = log2(r - l + 1); return sum[f[l][t]] > sum[f[r - (1 << t) + 1][t]]? f[l][t]: f[r - (1 << t) + 1][t]; } int main() { // freopen(".in", "r", stdin); scanf("%d%d%d%d", &n, &k, &L, &R); for (int i = 1; i <= n; i++) scanf ("%lld", &sum[i]), sum[i] += sum[i - 1], f[i][0] = i; for (int j = 1; j <= 20; j++) for (int i = 1; i + (1 << j - 1) <= n; i++) f[i][j] = sum[f[i][j-1]] > sum[f[i+(1<<j-1)][j-1]]? f[i][j-1]: f[i+(1<<j-1)][j-1]; for (int i = 1; i + L - 1 <= n; ++i) q.push((node){i, query(i + L - 1, min(i + R - 1, n)), i + L - 1, min(i + R - 1, n)}); for (; k--; ) { node x = q.top();q.pop(); ans += sum[x.y] - sum[x.x - 1]; // printf ("%lld\n", sum[x.x] - sum[x.y]); if (x.l != x.y) q.push((node){x.x, query(x.l, x.y - 1), x.l, x.y - 1}); if (x.r != x.y) q.push((node){x.x, query(x.y + 1, x.r), x.y + 1, x.r}); } printf ("%lld\n", ans); return 0; }