【HDOJ】【百度之星】5700 区间交

ProblemDescription 

A  有一个含有n 个非负整数的数列与 m  个区间。每个区间可以表示为l[i],r[i] 
它想选择其中 k  个区间, 使得这些区间的交的那些位置所对应的数的和最大。
例如样例中,选择[2,5]  [4,5]  两个区间就可以啦。

Input 

多组测试数据
第一行三个数 n,k,m(1n100000,1km100000) 
接下来一行 n  个数a[i] ,表示 lyk  的数列 (0a[i]10 9 ) 
接下来 m  行,每行两个数l[i],r[i] ,表示每个区间 (1l[i]r[i]n) 

Output 

一行表示答案

Source 

2016  ”百度之星” - 初赛( AstarRound2B 

区间题一般思路:定(枚举)区间左端点,计算(递推)区间右端点。

这题明显枚举区间交的左端点,并有一个显然的结论:交区间的左端点一定是某个区间的左端点(所有项非负)。

因为所有项都是非负的,所以定了左端点后,直接尽量长地延伸就好。
而且这是个区间交,所以选定的 k  个区间的左端点一定不在区间交的左端点右侧。

所以就枚举i ,查找左端点值小于等于 i  的区间中,第k 大的右端点是多少。(是不是很熟悉=w=),随便乱搞就好了。(博主维护了一个大小为 k  <script id="MathJax-Element-26" type="math/tex">k</script>的小根堆。)

#include<stdio.h>
#include<algorithm>
#define N 100050

int q[N],n,k,m,l;
long long s[N],ans;

struct seg{int l,r;}b[N];

inline void swap(int &a,int &b){a=a^b;b=a^b;a=a^b;}
inline bool cmp(const seg &a,const seg &b){return a.l<b.l;}

inline void up(int i){for (int j=i>>1;i>1;j=i>>1) if (q[i]<q[j]) swap(q[i],q[j]),i=j;else return;}

inline void down(int i)
{
    for (int j=i<<1;j<=l;j=i<<1)
    {
        if (q[j+1]<q[j] && j<l) j++;
        if (q[j]<q[i]) swap(q[i],q[j]),i=j;
        else return;
    }
}

int main()
{
    while (~scanf("%d%d%d",&n,&k,&m))
    {
        l=ans=0;
        for (int i=1,j;i<=n;i++) scanf("%d",&j),s[i]=s[i-1]+j;
        for (int i=0;i<m;i++) scanf("%d%d",&b[i].l,&b[i].r);
        std::sort(b,b+m,cmp);
        for (int i=0;i<m;i++)
        {
            if (l==k) {q[1]=q[l--],down(1);q[++l]=b[i].r,up(l);}
            else q[++l]=b[i].r,up(l);
            if (l==k) ans=(ans<s[q[1]]-s[b[i].l-1])?s[q[1]]-s[b[i].l-1]:ans;
        }
        printf("%I64d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值