区间交
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1263 Accepted Submission(s): 490
Problem Description
小A有一个含有n个非负整数的数列与m个区间。每个区间可以表示为li,ri。
它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大。
例如样例中,选择[2,5]与[4,5]两个区间就可以啦
多组测试数据
第一行三个数n,k,m(1≤n≤100000,1≤k≤m≤100000)。
接下来一行n个数ai,表示lyk的数列(0≤ai≤109)。
接下来m行,每行两个数li,ri,表示每个区间(1≤li≤ri≤n)。
Sample Input
5 2 3 1 2 3 4 6 4 5 2 5 1 4
Sample Output
10
这个题用了贪心的思想,思路是将左区间从小到大排序,从第k的左区间开始,查找这些区间中第k大的右区间,然后
记录该区间的值与最大做比较,从第k个左区依次类推,每次计算一次区间值之前都要先更新当前的右区间,可以用
线段树更新
#include<cstdio>
#include<cstring>
#include<algorithm>
#define max(x,y) x>y?x:y
using namespace std;
struct node
{
int l,r;
}a[100001];
int tree[300000];
long long sum[100001];
long long ans;
bool cmp(node c,node d)
{
return c.l<d.l;
}
void update(int l,int r,int node,int pos)
{
if(l==r)
{
tree[node]++;
return;
}
int m=(l+r)/2;
if(m>=pos)
update(l,m,node*2,pos);
else
update(m+1,r,node*2+1,pos);
tree[node]=tree[node*2]+tree[node*2+1];
}
int query(int l,int r,int node,int key)
{
if(l==r)
return l;
int m=(l+r)/2;
if(tree[node*2+1]>=key)
return query(m+1,r,node*2+1,key);
else
return query(l,m,node*2,key-tree[node*2+1]);//如果右子树保存的右区间数少于k,就在左子树寻找第k-tree[node]个右区间
}
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&k,&m))
{
int i,t;
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&t);
sum[i]=sum[i-1]+t;//计算前i个数的和
}
for(i=1;i<=m;i++)
scanf("%d%d",&a[i].l,&a[i].r);
sort(a+1,a+m+1,cmp);
memset(tree,0,sizeof(tree));//初始化线段树
for(i=1;i<k;i++)
{
update(1,n,1,a[i].r);//往线段树中插入右区间位置
}
ans=0;
for(i=k;i<=m;i++)
{
update(1,n,1,a[i].r);
t=query(1,n,1,k);//查找当前线段树保存的右区间中能最靠右的右区间
ans=max(ans,sum[t]-sum[a[i].l-1]);
}
printf("%lld\n",ans);
}
}