区间交(树状数组+二分)hdu5700

Problem Description
 

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

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

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

 

Input
 

多组测试数据

第一行三个数n,k,m(1n100000,1km100000)。

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

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

 

Output
 

一行表示答案


Sample Input
 
5 2 3
1 2 3 4 6
4 5
2 5
1 4
Sample Output
 
10

题意:


思路:

求相交区间和的最大值,首先是树状数组sum可以求log(n)求区间和,在找区间的时候枚举左端点,二分右端点,先把区间按左端点排序,然后一边更新一边询问,由于按左端点排序,所以左端点可以作为相交区间的左端点,二分右端点时询问这个点是否被大于等于k次覆盖,找到右端点最大的那个点,此时对应的区间就是这个左端点能得到的最大的区间,枚举完左端点就可以找到最大值了;
复杂度好像是mlog(n)log(n);


AC代码:



#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int nn=100005;
int n,k,m;
ll a[nn],sum[nn];  //a存放数据,sum求和
int num[nn];  //num为了代表区间位置
struct node{
	int l,r;
}pos[nn];
bool  cmp(node a,node b)
{
	if(a.l==b.l)return a.r>b.r;
	return a.l<b.l;
}
int lowbit(int x)
{
	return x&(-x);
}
void update(int x,ll y)   //此树状数组是为了求和
{
	while(x<=n)
	{
		sum[x]+=y;
		x+=lowbit(x);
	}
}
ll query(int pos)
{
	ll ans=0;
	while(pos>0)
	{
		ans+=sum[pos];
		pos-=lowbit(pos);
	}
	return ans;
}
void update1(int x,int flag)  //此树状数组是为了更新端点
{
	while(x<=n)
	{
		num[x]+=flag;
		x+=lowbit(x);
	}
}
int query1(int x)
{
	int ans=0;
	while(x>0)
	{
		ans+=num[x];
		x-=lowbit(x);
	}
	return ans;
}
int check(int x) //看有多少个右端点
{
	if(query1(x)>=k) return 1;
	return 0;
}
int main()
{
	ios::sync_with_stdio(false);
	while(cin>>n>>k>>m)	
	{
		ll ans=0;
		memset(num,0,sizeof(num));
		memset(sum,0,sizeof(sum));
		for(int i=1;i<=n;i++){
			cin>>a[i];
			update(i,a[i]);  //一边输入一边进行更新
		}
		for(int i=1;i<=m;i++)
		cin>>pos[i].l>>pos[i].r;
		sort(pos+1,pos+m+1,cmp);
		for(int i=1;i<=m;i++)
		{ 
			update1(pos[i].l,1);   //注意 在左边区间位置+1
			update1(pos[i].r+1,-1);//在右区间后一个位置—1,为了寻找端点,方便后面做题
			int L=pos[i].l,R=pos[i].r;
			while(L<=R)  //二分
			{
			int mid=(L+R)>>1;
			if(check(mid)) L=mid+1;
			else R=mid-1;	
			} 
			ll fx=query(L-1),fy;
			if(pos[i].l>1)fy=query(pos[i].l-1);
			else fy=0;
			ans=max(ans,fx-fy);  //找最大
			
		}
		cout<<ans<<endl;
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值