hdu 1540 Tunnel Warfare 线段树

这题目典型的线段树做法,但是网上很多人对每个节点都记录了3个值,左端起最长,右端起最长和中间最长,实际没有必要。

因为这题本质上我理解为是求所在序列的长度,举例,n为7,d 3 d 5,那么4所在的长度为1,1,2,3所在的长度为3。谈不上最大最小。只需要维护左端起最长的连续区间和右端起最长的连续区间即可。

对于查询,Q k,如果k在左儿子的右区间内并且k在右儿子的左区间内,那么就返回这两个区间的和,否则递归查询子区间。

而更新节点是递归返回时从底向上更新。

而找到R操作的点很简单,对于每个D操作就进栈,然后R操作就直接出栈顶即可。

还有一点,可能会有对重复点的删除和恢复,设置一个bool数组表示是否摧毁过该点即可。

代码不是很快,265ms:

#include<cstdio>
#include<cstring>
const int maxn = 5e4+9;
struct A{
	int ml,mr;
}stree[maxn<<2];
bool vi[maxn];
int stack[maxn];
int left,righ,k,q,top;
void buildTree(int rt,int l,int r){
	stree[rt].ml=stree[rt].mr=r-l+1;
	if(r>l){
		int mid=(l+r)>>1;
		buildTree(rt<<1,l,mid);
		buildTree(rt<<1|1,mid+1,r);
	}
}
inline void pushup(int rt,int l,int mid,int r){
	stree[rt].mr=stree[rt<<1].mr;
	stree[rt].ml=stree[rt<<1|1].ml;
	if(stree[rt].mr==mid-l+1)stree[rt].mr+=stree[rt<<1|1].mr;
	if(stree[rt].ml==r-mid)stree[rt].ml+=stree[rt<<1].ml;
}
void update(int rt,int l,int r,int v){
	int mid=(l+r)>>1;
	if(r>l){
		if(k<=mid)update(rt<<1,l,mid,v);
		else update(rt<<1|1,mid+1,r,v);	
		pushup(rt,l,mid,r);
	}else{
		stree[rt].ml=stree[rt].mr=v;
	}
	
}
int query(int rt,int l,int r){
	int mid=(l+r)>>1,ans=0;
	if(l<r){
		if(k>=mid+1-stree[rt<<1].ml&&k<=mid+stree[rt<<1|1].mr)
			ans=stree[rt<<1].ml+stree[rt<<1|1].mr;
		else{
			if(k<=mid)ans=query(rt<<1,l,mid);
			else ans=query(rt<<1|1,mid+1,r);
		}
	}
	return ans;
}
int main(){
	int n,m;
	while(scanf("%d%d",&n,&m)==2){
		top=0;
		memset(vi,0,sizeof(vi));
		buildTree(1,1,n);
		char s[2];
		while(m--){
			scanf("%s",s);
			if(*s=='D'){
				scanf("%d",&k);
				stack[top++]=k;
				if(!vi[k]){
					update(1,1,n,0);
					vi[k]=true;
				}
			}else if(*s=='R'){
				k=stack[--top];
				if(vi[k]){
					update(1,1,n,1);
					vi[k]=false;
				}
			}else{
				scanf("%d",&k);
				printf("%d\n",query(1,1,n));
			}
		}	
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值