hdu 5700 区间交 (线段树或优先队列)

区间交

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1423    Accepted Submission(s): 543


 

Problem Description

小A有一个含有n个非负整数的数列与m个区间。每个区间可以表示为li,ri。

它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大。

例如样例中,选择[2,5]与[4,5]两个区间就可以啦。

 

 

Input

多组测试数据

第一行三个数n,k,m(1≤n≤100000,1≤k≤m≤100000)。

接下来一行n个数ai,表示lyk的数列(0≤ai≤109)。

接下来m行,每行两个数li,ri,表示每个区间(1≤li≤ri≤n)。
 

 

 

Output

一行表示答案

 

 

Sample Input

 

5 2 3 1 2 3 4 6 4 5 2 5 1 4

 

 

Sample Output

 

10

思路:将左端点进行排序,然后我们枚举左端点作为区间交的左端点,要选择选k个区间,那么前k-1个就不用枚举了,然后找有右端点,我们可以发现越右边覆盖区间越长,用cover数组来标记出现了几次,然后用线段树来更新。当然也可以用优先队列来写思路差不多。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=100000+10;
int cover[N<<2];
ll sum[N<<2];
struct node
{
    int l,r;
    friend bool operator <(node a,node b)
    {
        return a.l<b.l;
    }
}a[N];
void update(int l,int r,int x,int k)
{
    if(l==r)
    {
        cover[x]++;
        return;
    }
    int mid=(l+r)/2;
    if(k<=mid)update(l,mid,x*2,k);
    else update(mid+1,r,x*2+1,k);
    cover[x]=cover[x*2]+cover[x*2+1];
}
int query(int l,int r,int x,int k)
{
    if(l==r)
        return l;
    int mid=(l+r)/2;
    if(cover[x*2+1]>=k)return query(mid+1,r,x*2+1,k);//优先从右边找
    else return query(l,mid,x*2,k-cover[x*2+1]);
}
int main()
{
    int n,k,m;
    while(~scanf("%d%d%d",&n,&k,&m))
    {
        memset(sum,0,sizeof(sum));
        memset(cover,0,sizeof(cover));
        for(int i=1;i<=n;i++)
        {
            ll x;
            scanf("%lld",&x);
            sum[i]=sum[i-1]+x;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
        }
        sort(a+1,a+m+1);
        for(int i=1;i<k;i++)
        {
            update(1,n,1,a[i].r);
        }
        ll ans=0;
        for(int i=k;i<=m;i++)
        {
            update(1,n,1,a[i].r);
            int qq=query(1,n,1,k);
            if(qq>=a[i].l)
            {
                ans=max(ans,sum[qq]-sum[a[i].l-1]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=100000+10;
struct node
{
    int l,r;
    friend bool operator < (node a,node b)
    {
       return  a.l<b.l;
    }
}a[maxn];
priority_queue<int,vector<int>,greater<int> >q;
ll sum[maxn];
int main()
{
    int n,k,m;
    while(~scanf("%d%d%d",&n,&k,&m))
    {
        while(!q.empty())q.pop();
        ll x;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&x);
            sum[i]=sum[i-1]+x;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
        }
        ll mx=0;
        sort(a+1,a+1+m);
        for(int i=1;i<=m;i++)
        {
            q.push(a[i].r);
            if(q.size()<k)continue;
            if(q.size()>k)q.pop();
            mx=max(mx,sum[q.top()]-sum[a[i].l-1]);
        }
        printf("%lld\n",mx);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值