题目传送门:Problem - 5700
样例输入:
6 3 5
3 4 1 0 5 6
1 3
2 6
3 5
5 6
1 2
样例输出:
5
中文题目,不做赘述。
解题思路:
通过观察,我们可以发现,得到的交区间左边界一定是某个区间的左边界,右边界一定是某个区间的右边界,这个时候,我们就可以选择固定右边界,枚举左边界为第k大的左边界,更新答案,这个时候,我们就需要对给定区间按右边界从小到大排序,只枚举1到m-k+1个,之所以m-k+1,就是为了保证区间交有k个,如果大了,显然枚举的区间不是k个区间的交集,因为如果是k个区间的交集,那么交集的右边界的右边一定还有k-1个右边界,同时为什么枚举的左边界一定是第k大的呢,首先第k大保证时k个区间交集,同时如果左边界比第k大还大,就不是当前右边界的最优结果,因为元素是大于0的,左边界尽量左,答案就肯定会更大。
思路有了,怎么做呢,我们首先要用个前缀和数组存储前缀和,以便计算区间交的和,同时用个树状数组存储左边界从小到大的个数,找第k大的时候只需对树状数组二分,接着就是枚举右边界,二分对应左边界,计算出答案之后将树状数组对应左边界位置-1,删除一个左边界的目的,初始化ans为0,没有交集的时候就输出的0。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include<algorithm>
using namespace std;
using LL=long long;
const int N=1e5+5;
struct node{
int l,r;
bool operator <(const node &n)const {
return r<n.r;
}
}q[N];
int a[N],tree[N],n;
LL sum[N];
int lowbit(int t)
{
return t&(-t);
}
void update(int idx,int val)
{
while(idx<=n)
{
tree[idx]+=val;
idx+=lowbit(idx);
}
}
LL query(int idx)
{
LL res=0;
while(idx)
{
res+=tree[idx];
idx-=lowbit(idx);
}
return res;
}
int ser(int k)
{
int l=1,r=n,mid;
while(l<r)
{
mid=(l+r)>>1;
if(query(mid)>=k) r=mid;
else l=mid+1;
}
return l;
}
int main()
{
int m,k;
while(scanf("%d%d%d",&n,&k,&m)!=EOF)
{
memset(tree, 0, sizeof(tree));
memset(sum, 0, sizeof(sum));
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",&q[i].l,&q[i].r);
}
sort(q+1, q+m+1);//按右边界从小到大排序
for (int i=1; i<=m; i++) {
update(q[i].l,1);//对应左边界位置+1,以达到二分查找第k大左边界的目的
}
LL ans=0;
int tem=m-k+1;//遍历长度
for (int i=1; i<=tem; i++) {
int idx=ser(k);//二分找右边界
ans=max(ans, sum[q[i].r]-sum[idx-1]);//计算答案,没有交集为负显然ans还为0
update(q[i].l, -1);//删除左边界
}
printf("%lld\n",ans);
}
return 0;
}