[ZJOI2007]捉迷藏

题目

  点这里看题目。

分析

  关于树上路径的统计问题,我们不难想到用点分治。而这道题存在修改,因此我们用 " 动态点分治 "。
  考虑正常的点分治,我们在每一层点分治中求的是经过当前点的最大关灯点距离。我们先求出本层中所有关灯点到自己的距离,并取其中来自不同子树的最大值和次大值,相加得到本层的答案。最后全局取最大值。
  现在通过数据结构让这个方法可以支持修改。可以发现,我们需要用一个结构维护当前层中来自不同子树的关灯点的距离的最大值和次大值。而来自不同子树的关灯点的距离实际上可以在分治的下一层中计算出来,也就是每一层维护一个当前层中所有关灯点到上一层分治中心的距离的最大值,用于更新上一层的前一个信息。
  发现这两个信息都只和最值有关,因此我们可以想到用堆维护。但是修改过程中需要删除旧有信息,因此我们需要用两个堆凑成一个 " 容器 " 实现惰性删除。一个堆存储已有值,另一个堆存储删除值。由于我们只需要保证得到的最值是正确的,因此我们可以在遇到两个堆堆顶相同时进行删除即可。
  本代码很慢......洛谷上面需要吸氧。

代码

#include <queue>
#include <cstdio>
using namespace std;

#define DEBUG( x ) printf( "%d", x )
#define DEBUG_( x ) printf( "%d ", x )
#define DEBUGn( x ) printf( "%d
", x )

const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 5, MAXLOG = 20;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

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

struct container
{
	priority_queue<int> cur, del;
	container() { while( ! cur.empty() ) cur.pop(); while( ! del.empty() ) del.pop(); }
	void clear() { while( ! cur.empty() ) cur.pop(); while( ! del.empty() ) del.pop(); }
	void cleaning() { while( ! cur.empty() && ! del.empty() && cur.top() == del.top() ) cur.pop(), del.pop(); }
	int top() { cleaning(); return cur.empty() ? -INF : cur.top(); }
	void insert( const int v ) { cur.push( v ); }
	void remove( const int v ) { del.push( v ); }
	int size() { cleaning(); return cur.size(); }
	
	int semiTop() 
	{
		if( size() <= 1 ) return -INF;
		int tmp = top(); cur.pop();
		int ret = top(); cur.push( tmp );
		return ret;
	}
};

container globe, mine[MAXN], fath[MAXN];
//globe为全局最优值

int f[MAXN][MAXLOG], dis[MAXN][MAXLOG];
int head[MAXN], lst[MAXN], dep[MAXN], siz[MAXN], mx[MAXN];
int N, M, tot, all, cnt;
bool vis[MAXN], statement[MAXN];
//statement存储是否有贡献,而非是否开灯

bool visible( const int u, const int fa ) { return ! vis[u] && u ^ fa; }

void addEdge( const int from, const int to )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt;
}

int getCen( const int u, const int fa )
{
	int ret = 0, tmp; siz[u] = 1, mx[u] = 0;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( visible( v = Graph[i].to, fa ) )
		{
			tmp = getCen( v, u );
			siz[u] += siz[v], mx[u] = MAX( mx[u], siz[v] );
			if( mx[tmp] < mx[ret] ) ret = tmp;
		}
	if( ( mx[u] = MAX( mx[u], all - siz[u] ) ) < mx[ret] ) ret = u;
	return ret;
}

void DFS( const int u, const int fa, const int rt, const int d )
{
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( visible( v = Graph[i].to, fa ) )
			dis[v][++ dep[v]] = d, f[v][dep[v]] = rt, DFS( v, u, rt, d + 1 );
}

void divide( const int u )
{
	vis[u] = true; DFS( u, 0, u, 1 ); int tmp = all;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( ! vis[v = Graph[i].to] )
			all = siz[v] > siz[u] ? tmp - siz[u] : siz[v],
			divide( getCen( v, u ) );
}

void gblyRemove( const int u ) 
{
	int cur = mine[u].top() + mine[u].semiTop(); 
	if( mine[u].size() > 1 && cur == lst[u] ) globe.remove( lst[u] ), lst[u] = -1; 
}

void gblyInsert( const int u ) 
{ 
	int cur = mine[u].top() + mine[u].semiTop(); 
	if( mine[u].size() > 1 && cur ^ lst[u] ) globe.insert( lst[u] = cur ); 
}

void change( const int u )
{
	if( statement[u] )
	{
		tot --, statement[u] = false;
		for( int i = dep[u] + 1 ; i ; i -- ) gblyRemove( f[u][i] );
		mine[u].remove( 0 );
		for( int cur, i = dep[u] ; i ; i -- )
		{
			cur = f[u][i];
			mine[cur].remove( fath[f[u][i + 1]].top() );
			fath[f[u][i + 1]].remove( dis[u][i] );
			mine[cur].insert( fath[f[u][i + 1]].top() );
			//更新mine和fath
		}
		for( int i = dep[u] + 1 ; i ; i -- ) gblyInsert( f[u][i] );
		//前后统一进行全局更新
	}
	else
	{
		tot ++, statement[u] = true;
		for( int i = dep[u] + 1 ; i ; i -- ) gblyRemove( f[u][i] );
		mine[u].insert( 0 );
		for( int cur, i = dep[u] ; i ; i -- )
		{
			cur = f[u][i];
			mine[cur].remove( fath[f[u][i + 1]].top() );
			fath[f[u][i + 1]].insert( dis[u][i] );
			mine[cur].insert( fath[f[u][i + 1]].top() );
		}
		for( int i = dep[u] + 1 ; i ; i -- ) gblyInsert( f[u][i] );
	}
}

int main()
{
	read( N );
	for( int i = 1, a, b ; i < N ; i ++ ) read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
	mx[0] = all = N; 
	divide( getCen( 1, 0 ) );
	for( int i = 1 ; i <= N ; i ++ ) f[i][dep[i] + 1] = i, change( i );
	read( M );
	char op[5]; int p;
	while( M -- )
	{
		scanf( "%s", op );
		if( op[0] == 'C' ) read( p ), change( p );
		else
		{
			if( tot == 0 ) puts( "-1" );
			else if( tot == 1 ) puts( "0" );
			else write( globe.top() ), putchar( '
' );
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/12583893.html