题目链接:区间交
Sample Input
5 2 3
1 2 3 4 6
4 5
2 5
1 4
Sample Output
10
题意
有n个区间,求k个区间,使得这k个区间相交的区间内数字之和最大。数列的数字均>=0。
Solution
先按照区间的右区间位置排序,枚举右端点,对于每个右端点,查找左端点第k小,左右端点间元素相加,更新答案
取最大即可。注意每次枚举右端点时将上一次区间的左端点删除,即使该次区间右端点与上次右端点相等,删了上次
左端点对也能得出最终正确答案。注意到n不是很大,对于区间左端点组成的元素,可以用树状数组二分查找维护第k小。
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int N=1e5;
struct node{
int l,r,id;
bool operator <(const node &n)const {
return r<n.r;
}
}no[N+2];
int a[N+2],c[N+2],n;
ll sum[N+2];
int lowbit(int i){
return i&(-i);
}
void add(int i,int v){
while(i<=n){
c[i]+=v;
i+=lowbit(i);
}
}
ll getsum(int i){
ll ans=0;
while(i>0){
ans+=c[i];
i-=lowbit(i);
}
return ans;
}
int find_kth(int k){ //二分,查找区间左端点第k小
int l=1,r=n,mid;
while(l<r){
mid=(l+r)>>1;
if(getsum(mid)>=k)
r=mid;
else
l=mid+1;
}
return l;
}
int main(){
int m,k;
while(~scanf("%d%d%d",&n,&k,&m)){
memset(sum,0,sizeof(sum));
memset(c,0,sizeof(c));
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=m;++i)
scanf("%d%d",&no[i].l,&no[i].r);
sort(no+1,no+m+1);
for(int i=1;i<=m;++i)
add(no[i].l,1);
int tag=m-k+1;
ll ans=0;
for(int i=1;i<=tag;++i){
int index=find_kth(k);
ans=max(ans,sum[no[i].r]-sum[index-1]);
add(no[i].l,-1);
}
cout<<ans<<endl;
}
return 0;
}