2019牛客多校赛第十场F.Popping Balloons(线段树)

题目链接

题意:在一个n*n的二维空间上有任意个气球,你可以横着发射三发子弹,竖着发射三发子弹,且要求每连续两发子弹的间距要恰好等于r。每发子弹可以打掉一排或者一列的气球,问你最多可以打掉多少个气球。

题解:这题思路其实不难想。将每连续三行的气球合并放在线段树里,维护气球个数最大值。然后暴力枚举每三列气球,每次先将这三列的气球在线段树去掉,更新后再求得行最大值,就是答案,然后再把删除的点的更新回去。比赛的时候全队以为这样的时间复杂度是n^2logn,但其实不是。采用势能分析法,在线段树上每次删除一个点会影响到 X   X-R   X-2*R  3个值 最多操作1e5次 复杂度 6*n*logn(删了还要加回来。

#include
using namespace std;
const int N = 1e5+10;
vectorf1[N],f2[N];
int mx[4*N];
void up(int id,int l,int r,int pos,int val){
	if(l==r){
		mx[id]+=val;
		return;
	}
	int mid = l+r>>1;
	if(pos<=mid) up(id<<1,l,mid,pos,val);
	else up(id<<1|1,mid+1,r,pos,val);
	mx[id]=max(mx[id<<1],mx[id<<1|1]);
}
int main()
{
	int n,r;
	scanf("%d%d",&n,&r);
	for(int i=1;i<=n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		f1[x+1].push_back(y+1);
		f2[y+1].push_back(x+1);
	}
	for(int i=1;i<=100001;++i){//每个左端点能贡献的答案 
		int val=f2[i].size();
		if(i+r<=100001) val+=f2[i+r].size();
		if(i+2*r<=100001) val+=f2[i+2*r].size();
		up(1,1,100001,i,val);
	}
	int ans=0;
	for(int i=1;i<=100001;++i){
		if(f1[i].size()==0) continue;
		int tmp=0,cnt=0;
		for(int j=i;j>=1;j-=r){//将枚举三列的点删掉 
			if(cnt==3||j<1) break;
			tmp+=f1[j].size();
			cnt++;
			for(int v:f1[j]){
				up(1,1,100001,v,-1);
				if(v-r>=1) up(1,1,100001,v-r,-1);
				if(v-2*r>=1) up(1,1,100001,v-2*r,-1);
			}
		}
		tmp+=mx[1];//加上线段树最大值 
		cnt=0;
		for(int j=i;j>=1;j-=r){//将枚举三列的点添加 
			if(cnt==3||j<1) break;
			cnt++;
			for(int v:f1[j]){
				up(1,1,100001,v,1);
				if(v-r>=1) up(1,1,100001,v-r,1);
				if(v-2*r>=1) up(1,1,100001,v-2*r,1);
			}
		}
		ans=max(ans,tmp);
	}
	printf("%d\n",ans);
}

 

你可能感兴趣的:(线段树,牛课多校)