HDU 5700 区间交 离线线段树

区间交

题目连接:

http://acm.hdu.edu.cn/showproblem.php?pid=5700

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

Hint

题意

题解:

离线线段树,线段树维护区间和就好了

我暴力枚举左端点,然后每次使得在这个左端点上的区间的右端点++就好了

那么显然从i开始到这个位置的前缀和,就是这个点覆盖了多少次

那么我们通过在线段树上二分去找这个点就好了

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int n,k,m;
long long t[maxn*4],sum[maxn],a[maxn],l,r;
vector<int> E[maxn];
void build(int x,int l,int r)
{
    t[x]=0;
    if(l==r)return;
    int mid=(l+r)/2;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
void update(int x,int l,int r,int pos)
{
    if(l==r)
    {
        t[x]++;
        return;
    }
    int mid=(l+r)/2;
    if(pos<=mid)update(x<<1,l,mid,pos);
    else update(x<<1|1,mid+1,r,pos);
    t[x]=t[x<<1]+t[x<<1|1];
}
int query(int x,int l,int r,int p)
{
    if(t[x]<p)return -1;
    if(l==r)return l;
    int mid=(l+r)/2;
    if(p<=t[x<<1|1])return query(x<<1|1,mid+1,r,p);
    else return query(x<<1,l,mid,p-t[x<<1|1]);
}
void solve()
{
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
        E[i].clear();
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        E[l].push_back(r);
    }
    long long ans = 0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<E[i].size();j++)update(1,1,n,E[i][j]);
        int p=query(1,1,n,k);
        if(p>=i)ans=max(ans,sum[p]-sum[i-1]);
    }
    printf("%lld\n",ans);
}
int main()
{
    while(cin>>n>>k>>m)
        solve();
}
posted @ 2016-05-24 10:30  qscqesze  阅读(512)  评论(0编辑  收藏  举报