HDU 5700 区间交 树状数组+二分

题目传送门: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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值