Tunnel Warfare HDU - 1540 线段树 连续区间

本文提供了一道 HDU 1540 的算法题解,使用了线段树来求解最大连续区间的问题。通过构建线段树并进行更新和查询操作,实现了对村庄破坏状态的有效跟踪。

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=1540


思路:

1.注意一下恢复的情况,村庄可以被破坏多次,只需要一次就可以恢复

2.最大连续区间  注释很详细了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std; 

const int N = 5e4+10; 
struct node{
	int l,r;
	int ms,ls,rs; 
	// ls是左区间的长度,rs是右区间长度,ms是区间里面的最大长度 
	int mid() {  
		return (l+r)/2; 
	}
};
node num[N*4]; 

void build( int root,int l,int r ) 
{
	num[root].l = l ; 
	num[root].r = r; 
	num[root].ms = num[root].ls = num[root].rs = r-l+1; 
	if ( l==r ) return ; 
	build( root*2,  l,num[root].mid() );
	build( root*2+1, num[root].mid()+1 , r );
	return ; 
}

void update( int root,int pos,int op ) {
	if ( num[root].l==num[root].r ) {
		num[root].ls = num[root].rs = num[root].ms = op ; 
		return ;
	}
	if ( pos<=num[root].mid() ) update( root*2,pos,op ); 
	else update( root*2+1,pos,op ) ; 
	
	num[root].ls = num[root*2].ls; //root左区间 是左子树的左区间的长度 
	num[root].rs = num[root*2+1].rs;  //root右区间 是右子树的右区间的长度 
	if ( num[root*2].r-num[root*2].ls+1==num[root*2].l ) 
		num[root].ls += num[root*2+1].ls; 	//如果左子树的区间长度覆盖整个左子树的长度,它就和右子树的左区间连在了一起,所以加上右子树的左区间 
	if ( num[root*2+1].r-num[root*2+1].rs+1==num[root*2+1].l ) 
		num[root].rs += num[root*2].rs; 
	num[root].ms = max( max( num[root*2].ms,num[root*2+1].ms) , num[root*2].rs+num[root*2+1].ls ); 
	return ; 
}

int query( int root,int pos ) 
{
	//如果区间的最大长度是0   或者  查找到了根节点  或者  区间最大连续长度就是整个区间的长度  就返回 
	if ( num[root].ms==0 || (num[root].ms==num[root].r-num[root].l+1) || num[root].l==num[root].r )
		return num[root].ms; 
		
	if ( pos<=num[root].mid() ) {
		//如果pos在左子树的右区间里面,那么它的连续长度需要加上右子树的第一个值的连续长度. 
		if ( pos>=num[root*2].r-num[root*2].rs+1 )  
			return query( root*2,pos ) + query( root*2+1,num[root].mid()+1 ) ; 
		//	return query( root*2,pos ) + num[root*2+1].ls ; 和上一句是一模一样的效果 
			
		else return query( root*2,pos ); 
	}
	else {
		if ( pos <= num[root*2+1].l+num[root*2+1].ls-1 )  
			return query( root*2+1,pos ) + query( root*2,num[root].mid() ) ;  ///mid-1
		//	return query( root*2+1,pos ) + num[root*2].rs ;  和上一句是一模一样的效果 
		else return query( root*2+1,pos ); 
	}
}
int n,q; 
bool vis[N]; 
int s[N];
int top=0; 
char c; 
int main()
{
	int temp,ans; 
	
	while ( scanf("%d%d",&n,&q)!=EOF ) {
		memset( vis,false,sizeof(vis) ); 
		top = 0; //!!!!!
		build( 1,1,n); 
		while ( q-- ) {
			scanf(" %c",&c);
			if ( c=='D' ) {
				scanf("%d",&temp); 
				vis[temp] = 1; 
				s[top++] = temp; 
				update( 1,temp,0 );	 
			}
			else if ( c=='R' ) {
				temp = s[--top]; 
				if ( vis[temp] ) {
					update( 1,temp,1 ) ; 
					vis[temp] = 0; 
				}
				else temp = s[--top]; 
			}
			else {
				scanf("%d",&temp); 
				ans = query( 1,temp ) ; 
				cout<<ans<<endl ;
			}
		}	
	} 
	
	return 0 ; 
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值