BZOJ1095: [ZJOI2007]Hide 捉迷藏

Description

  捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩
捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋
子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的
时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要
求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两
个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房
间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的
距离。

Input

  第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,
表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如
上文所示。

Output

  对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关
着灯的,输出0;若所有房间的灯都开着,输出-1。

Sample Input

8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G

Sample Output

4
3
3
4

HINT

对于100%的数据, N ≤100000, M ≤500000。

Source

线段树+括号序列
用括号序列表示一棵树的方法类似于dfs序,进入时加入一个[,访问完时加入一个]
然后两个点之间的距离就是未匹配括号数
发现一定匹配的消去之后一定长这样]]]]]]]][[[[[[[[[[
然后线段树维护即可,为了维护答案还要维护到两边的一些东西,具体见代码
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 100010;
const int INF = 1e9 + 7;

inline int read()
{
	int sc = 0; char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' ) sc = sc * 10 + ch - '0', ch = getchar();
	return sc;
}

struct edge
{
	int to, nxt;
}e[MAXN << 1];

int n, Q, head[MAXN], cnt, pos[MAXN], tot, num[MAXN * 3], vis[MAXN], now;

char opt[2];

inline void add(int x, int y) { e[ ++cnt ].to = y; e[ cnt ].nxt = head[ x ]; head[ x ] = cnt; }
inline void addedge(int x, int y) { add( x, y ); add( y, x ); }

inline void dfs(int x, int fa)
{
	num[ ++tot ] = -1;
	pos[ num[ ++tot ] = x ] = tot;
	for( int i = head[ x ] ; i ; i = e[ i ].nxt ) if( e[ i ].to ^ fa ) dfs( e[ i ].to, x );
	num[ ++tot ] = -2;
}

struct node
{
	int c1, c2, l1, l2, r1, r2, ans;
	//c1---]                c2---[
	//l1---max( ] + [ )     l2---max( [ - ] )
	//r1---max( ] + [ )     r2---max( ] - [ )
	inline void cal(int x)
	{
		c1 = c2 = 0;
		l1 = l2 = r1 = r2 = ans = -INF;
		if( x == -1 ) c2 = 1;
		else if( x == -2 ) c1 = 1;
		else if( vis[ x ] == 1 ) l1 = l2 = r1 = r2 = 0;
	}
}t[MAXN * 3 << 2];

inline node merge(node a, node b)
{
	node c;
	c.c1 = a.c1 + max( 0, b.c1 - a.c2 );
	c.c2 = b.c2 + max( 0, a.c2 - b.c1 );
	c.ans = max( max( a.ans, b.ans ), max( a.r1 + b.l2, a.r2 + b.l1 ) );
	c.l1 = max( a.l1, max( b.l1 - a.c2 + a.c1, b.l2 + a.c1 + a.c2 ) );
	c.l2 = max( a.l2, b.l2 + a.c2 - a.c1 );
	c.r1 = max( b.r1, max( a.r1 - b.c1 + b.c2, a.r2 + b.c1 + b.c2 ) );
	c.r2 = max( b.r2, a.r2 + b.c1 - b.c2 );
	return c;
}

inline void update(int x, int l, int r, int pos)
{
	if( l == r ) { t[ x ].cal( num[ l ] ); return ; }
	int mid = l + r >> 1;
	if( pos <= mid ) update( x << 1, l, mid, pos );
	else update( x << 1 | 1, mid + 1, r, pos );
	t[ x ] = merge( t[ x << 1 ], t[ x << 1 | 1 ] );
}

inline void build(int x, int l, int r)
{
	if( l == r )
	{
		t[ x ].cal( num[ l ] );
		return ; 
	}
	int mid = l + r >> 1;
	build( x << 1, l, mid );
	build( x << 1 | 1, mid + 1, r );
	t[ x ] = merge( t[ x << 1 ], t[ x << 1 | 1 ] );
}

int main()
{
	now = n = read();
	for( int i = 1 ; i <= n ; i++ ) vis[ i ] = 1;
	for( int i = 1 ; i < n ; i++ ) addedge( read(), read() );
	dfs( 1, 0 );
	build( 1, 1, tot );
	Q = read();
	while( Q-- )
	{
		scanf( "%s", opt );
		if( opt[ 0 ] == 'G' )
		{
			if( !now ) puts( "-1" );
			else if( now == 1 ) puts( "0" );
			else printf( "%d\n", t[ 1 ].ans );
		}
		else
		{
			int x = read();
			now += vis[ x ] = -vis[ x ];
			update( 1, 1, tot, pos[ x ] ); 
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值