现在的位置: 首页 > 综合 > 正文

poj 2892 Tunnel Warfare(线段树 单点更新 区间合并)

2014年04月05日 ⁄ 综合 ⁄ 共 2113字 ⁄ 字号 评论关闭

http://poj.org/problem?id=2892

题意:有n个连续的村庄,编号1~n,有三种操作:

D X:摧毁X村庄; R:修复后摧毁的村庄(后摧毁的先修复);Q X:询问与X村庄有直接或间接相连的村庄数。


思路:与poj 3667类似,都是求满足条件的连续最长区间,属于区间合并问题。不过这个相对简单,因为这个是单点更新,不用考虑延迟操作。求最长连续区间长度时,一般节点增加三个域,lx,rx,ax,分别表示从左边数最长连续区间,从右边数最长连续区间,整个区间中满足条件的最长连续区间。lx,rx是为了在子区间合并时,方便更新,也方便查询。


单点更新时,更新完这个点后,要UP上去,即也要更新它的父亲节点,包括lx,rx,ax。

查询某个点所在的满足条件的最长区间长度时,首先要弄清递归终止的条件:该区间长度为1或该区间全部为空或全部不为空,因为当前区间全为空或全不空时,其子区间也一定是这样的状态,所以不必继续递归,直接返回该区间的长度即可。若上面没走掉,就继续递归。具体思路见代码解释。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
using namespace std;

const int maxn = 50005;

struct line
{
	int l,r;
	int lx,rx,ax;
}tree[maxn<<2];

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].lx = tree[v].rx = tree[v].ax = r-l+1;
	if(l == r)
		return;
	int mid = (l+r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
}

//将pos节点状态更新为flag
void update(int v, int pos, int flag)
{
	if(tree[v].l == tree[v].r)//找到该节点,直接更新
	{
		tree[v].lx = tree[v].rx = tree[v].ax = flag;
		return;
	}
	int mid = (tree[v].l+tree[v].r)>>1;
	if(pos <= mid)
		update(v*2,pos,flag);	//递归左子树
	else update(v*2+1,pos,flag);//递归右子树
	
	//更新完这个节点后要把新信息UP上来
	if(tree[v*2].ax == tree[v*2].r-tree[v*2].l+1)
		tree[v].lx = tree[v*2].ax + tree[v*2+1].lx;
	else tree[v].lx = tree[v*2].lx;

	if(tree[v*2+1].ax == tree[v*2+1].r-tree[v*2+1].l+1)
		tree[v].rx = tree[v*2+1].ax + tree[v*2].rx;
	else tree[v].rx = tree[v*2+1].rx;

	tree[v].ax = max(max(tree[v*2].ax,tree[v*2+1].ax),tree[v*2].rx+tree[v*2+1].lx);
}

//询问pos所在区间满足条件的最长连续区间长度
int query(int v, int pos)
{
	if(tree[v].l == tree[v].r || tree[v].ax == 0 || tree[v].ax == tree[v].r-tree[v].l+1)
		return tree[v].ax;//递归终止条件,当该区间全空或全满的时候递归终止,不必访问子节点,直接返回。

	int mid = (tree[v].l+tree[v].r)>>1;
	if(pos <= mid)//pos在左子树
	{
		if(pos >= tree[v*2].r-tree[v*2].rx+1)
			return tree[v*2].rx+tree[v*2+1].lx;
		else return query(v*2,pos);
	}
	else		  //pos在右子树
	{
		if(pos <= tree[v*2+1].l + tree[v*2+1].lx -1)
			return tree[v*2].rx+tree[v*2+1].lx;
		else return query(v*2+1,pos);
	}
}

int main()
{
	int n,m,x;
	char s[2];
	stack <int> st;
	while(!st.empty()) st.pop();
	scanf("%d %d",&n,&m);
	build(1,1,n);

	while(m--)
	{
		scanf("%s",s);
		if(s[0] == 'D')
		{
			scanf("%d",&x);
			st.push(x);
			update(1,x,0);//把X节点的状态更新为0.
		}
		else if(s[0] == 'R')
		{
			x = st.top();
			st.pop();
			update(1,x,1);//把X节点的状态更新为1.
		}
		else
		{
			scanf("%d",&x);
			int ans = query(1,x);//询问X节点所在区间的满足题意的最长连续区间长度
			printf("%d\n",ans);
		}
	}
	return 0;
}

抱歉!评论已关闭.