HDU-1540(线段树&区间合并&最大连续区间)

做kuangbin线段树专题的时候遇到的题目,感觉这是一道非常有趣的题目。线段树的题目做的比较少,没有见过这种通过线段树在区间上做文章的题目,感觉做完这个题目之后加深了对线段树左右孩子所覆盖的区间之间的关系的认识。

题意:1-n个地道,m个次操作,D代表摧毁第i个地道,Q代表查询包含第i个地道的最大连续地道数目,并输出。R代表修复最近摧毁的那个地道;

解题的关键思路&过程:这个题目一开始我完全想不到利用左右孩子所覆盖的区间之间的关系来做到查询最大连续区间,感觉这个题目和线段树基本操作的对线段树的所利用的性质不同,这里充分利用了相邻的节点之间区间都是连续的这个性质,这个虽然是个基本的性质,但是平时着重与对左右孩子之间关系利用,有点忽视非相同父节点的同一层的叶子节点之间的关系(菜~_~)。这个题目利用tree[i].ll和tree[i].rr这两个元素分别记录这个节点所包含的区间从最左边起的最大连续区间,和从最右边起的最大连续区间

想一下为什么要这么做呢??

感觉这正是巧妙的地方,正是这样就可以求得同一层叶子节点的最大连续区间,同层的最大连续区间只可能是你包含你查询的那个地道i的区间的节点,加上左边那个节点的的最左边起的最大连续区间或着加上右边那个节点的从最右边起的最大连续区间

再想一下为什么最大连续区间只能是最多相邻两个节点的加和呢?因为如果当前层有三个节点的区间都连续,那查找时一定会在当前层的上一层节点就结束,不会再查到此层

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
struct node{
	int l,r;
	int ll,rr,len;
}tree[4*50050];
void build(int i,int l,int r){
	tree[i].l=l;tree[i].r=r;
	tree[i].ll=tree[i].rr=r-l+1;
	tree[i].len=r-l+1;
	if(l==r){
		return;
	}
	build(i<<1,l,(int)floor((r+l)/2.0));
	build((i<<1)+1,(int)floor((r+l)/2.0)+1,r);
}
void update(int i,int x,int cnt){
	if(tree[i].l==tree[i].r){
		tree[i].ll=tree[i].rr=cnt;
		return;
	}
	int mid=(tree[i].l+tree[i].r)>>1;
	if(mid>=x){
		update(i<<1,x,cnt);
	}
	else{
		update((i<<1)+1,x,cnt);
	}
	if(tree[(i<<1)].ll+tree[(i<<1)+1].rr==tree[i].len)
		tree[i].ll=tree[i].rr=tree[i].len;
	else{
		if(tree[i<<1].ll==tree[i<<1].len)
			tree[i].ll=tree[i<<1].len+tree[(i<<1)+1].ll;
		else
			tree[i].ll=tree[i<<1].ll;
		if(tree[(i<<1)+1].rr==tree[(i<<1)+1].len)
			tree[i].rr=tree[(i<<1)+1].len+tree[i<<1].rr;
		else
			tree[i].rr=tree[(i<<1)+1].rr;
	}
}
int que(int i,int x){
	if(i==1){
		if(tree[i].ll){
			if((tree[i].l+tree[i].ll-1)>=x)
				return tree[i].ll;
		}
		if(tree[i].rr){
			if((tree[i].r-tree[i].rr+1)<=x)
				return tree[i].rr; 
		}
	}
	if(tree[i].ll){
		if((tree[i].l+tree[i].ll-1)>=x)
			return tree[i].ll+tree[i-1].rr;		
	}
	if(tree[i].rr){
		if((tree[i].r-tree[i].rr+1)<=x)
			return tree[i].rr+tree[i+1].ll;
	}
	if(tree[i].l==tree[i].r)
		return 0;
	int mid=(tree[i].l+tree[i].r)>>1;
	if(mid>=x)
		return que(i<<1,x);
	else 
		return que((i<<1)+1,x);
}
int main(){
	int n,m;
	int q[50005];
	while(cin>>n>>m){
		build(1,1,n);
		int flag=0;
		for(int i=1;i<=m;i++){
			char c[5];
			cin>>c;
			if(c[0]=='D'){
				int k;
				cin>>k;
				q[++flag]=k;
				update(1,k,0);
			}
			else if(c[0]=='Q'){
				int k;
				cin>>k;
				cout<<que(1,k)<<endl;
			}
			else{
				if(flag==0)continue;
				int k=q[flag--];
				update(1,k,1);  
			}
		}
	}
	return 0;
}

 

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值