題目鏈接https://www.51nod.com/Challenge/Problem.html#problemId=1672
小A有一個含有n個非負整數的數列與m個區間,每個區間可以表示爲li,ri。
它想選擇其中k個區間, 使得這些區間的交的那些位置所對應的數的和最大。(是指k個區間共同的交,即每個區間都包含這一段,具體可以參照樣例)
在樣例中,5個位置對應的值分別爲1,2,3,4,6,那麼選擇[2,5]與[4,5]兩個區間的區間交爲[4,5],它的值的和爲10。
輸入
第一行三個數n,k,m(1<=n<=100000,1<=k<=m<=100000)。
接下來一行n個數ai,表示小A的數列(0<=ai<=10^9)。
接下來m行,每行兩個數li,ri,表示每個區間(1<=li<=ri<=n)。
輸出
一行表示答案
輸入樣例
5 2 3
1 2 3 4 6
4 5
2 5
1 4
輸出樣例
10
很顯然,我們可以直接枚舉區間[l,r],那麼答案就是其中的某個區間。而很顯然,我們可以直接枚舉左端點l,然後對於在這個區間內的小區間它絕對滿足L<=l並且R>=l的,也就是說對於滿足條件的小區間,我們將他們扔到線段樹操作,如果這個區間被大於等於k個區間覆蓋,那麼我們需要找到對於左端點l而言被k個區間覆蓋的最大的R(這樣就可以使得覆蓋的元素更多和也更大),也就是找當前符合區間的最大R。那麼直接用權值線段樹就可以找到該位置了。但有一個問題就是我們怎麼判斷哪個區間滿足條件,最暴力的方法就是在枚舉l的同時對每個區間枚舉,然後判斷。。。果斷T飛,實際上我們可以注意到的是,我們的l是從小到大枚舉的,那麼我們對每個區間的左端點排個序就好了,然後記錄一下每個左端點所對應的的右端點的值,以及右端點對應的右端點的值:
for (int i=1; i<=m; i++){
int l,r;
scanf ("%d%d",&l,&r);
st[l].push_back(r);ed[r].push_back(r);
}
那麼我們從1到n枚舉區間的時候就可以很快地判斷了。我們每次枚舉l的時候,在線段樹中插入以l爲左端點的區間(實際上只放了該區間的右端點),然後刪除已l爲右端點的區間及update(st[l],1),update(ed[l],-1),這樣的區間枚舉就是O(n)的複雜度了,在權值線段樹中找到最大的R然後用前綴和相減一下就是k個區間以l爲左端點的最大元素和了。講得不太好QAQ。。。。代碼混着文字說明應該更容易理解。
以下是AC代碼:
#include <bits/stdc++.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
const int mac=1e5+10;
int tree[mac<<2];
void update(int l,int r,int rt,int pos,int val)
{
if (l==r){
tree[rt]+=val;
return;
}
int mid=(l+r)>>1;
if (mid>=pos) update(lson,pos,val);
else update(rson,pos,val);
tree[rt]=tree[ls]+tree[rs];
}
int query(int l,int r,int rt,int val)
{
if (l==r) return l;
int mid=(l+r)>>1;
if (tree[rs]>=val) return query(rson,val);
else return query(lson,val-tree[rs]);
}
vector<int>st[mac],ed[mac];
ll sum[mac];
int main()
{
int n,k,m;
while (scanf ("%d%d%d",&n,&k,&m)!=EOF){
for (int i=1; i<=n; i++){
st[i].clear();ed[i].clear();
int x;
scanf ("%d",&x);
sum[i]+=sum[i-1]+x;
}
for (int i=1; i<=m; i++){
int l,r;
scanf ("%d%d",&l,&r);
st[l].push_back(r);ed[r].push_back(r);
}
ll ans=0;
for (int i=1; i<=n; i++){
for (int j=0; j<st[i].size(); j++)
update(1,n,1,st[i][j],1);
if (tree[1]>=k){
int pos=query(1,n,1,k);
ans=max(ans,sum[pos]-sum[i-1]);
}
for (int j=0; j<ed[i].size(); j++)
update(1,n,1,ed[i][j],-1);
}
memset(tree,0,sizeof tree);
printf ("%lld\n",ans);
}
return 0;
}