分析:
从左往右扫,如果进入一个区间的左端点,计数器加一,出去一个区间的又端点,计数器减一。这样就能在
O(N)
的时间内判断出可行的区间。
因为a[i]>=0,所以需要让区间的交集的长度尽可能大。
考虑每次从一个区间的右端点出来的情况。假设当前区间被使用到。现在确定了右端点,只需要找左端点在哪里就行了。因为左端点要尽可能远离右端点,所以在所有覆盖当前右端点的区间中找一个第K小的左端点就行了。用线段树来维护所有覆盖当前点的区间的左端点,每次在走出一个区间的时候查找第K小的左端点,同时和此刻走出的区间的左端点比较去较大,就能确定区间的范围了。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <map>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
int sum[maxn << 2];
long long presum[maxn];
int a[maxn];
#define lson l, m, v << 1
#define rson m + 1, r, v << 1 | 1
struct Node {
int l, r;
}node[maxn];
struct vnode {
int val, id;
bool operator <(const vnode &rhs) const {
return val < rhs.val;
}
}ll[maxn], rr[maxn];
void PushUp(int v) {
sum[v] = sum[v << 1] + sum[v << 1 | 1];
}
void update(int p, int x, int l, int r, int v) {
if (l == r) sum[v] += x;
else {
int m = (l + r) >> 1;
if (p <= m) update(p, x, lson);
else update(p, x, rson);
PushUp(v);
}
}
int query(int k, int l, int r, int v) {
if (l == r) return l;
int ans = sum[v << 1];
int m = (l + r) >> 1;
if (k > ans) return query(k - ans, rson);
else return query(k, lson);
}
int main(int argc, char const *argv[]) {
int n, k, m;
cin>>n>>k>>m;
for (int i = 1; i <= n; i ++) {
scanf("%d", a + i);
presum[i] = presum[i - 1] + (long long)a[i];
}
for (int i = 1; i <= m; i ++) {
scanf("%d%d", &node[i].l, &node[i].r);
ll[i].val = node[i].l, ll[i].id = i;
rr[i].val = node[i].r, rr[i].id = i;
}
sort(ll + 1, ll + m + 1);
sort(rr + 1, rr + m + 1);
int p = 1, q = 1, tmpk = 0;
long long ans = 0;
for (int i = 1; i <= n; i ++) {
while (ll[p].val == i) {
update(i, 1, 1, n, 1);
p ++;
tmpk ++;
}
while (rr[q].val == i) {
int id = rr[q].id;
if (tmpk >= k) {
int tmpl = query(k, 1, n, 1);
tmpl = max(node[id].l, tmpl);
ans = max(ans, presum[i] - presum[tmpl - 1]);
}
update(node[id].l, -1, 1, n, 1);
q ++;
tmpk --;
}
}
cout<<ans<<endl;
return 0;
}