[HDU6334] Problem C. Problems on a Tree

题目

校内赛的改编题目。题意基本与[HDU6334] Problem C. Problems on a Tree相同。

分析

简单分析就可以发现,当 (s) 确定的时候,一个点 (t) 可以到达 (s) ,必须满足 (t)(s) 的路径上,要么没有 3 边,要么仅有一条 3 边,且 (s) 到一端的路径上只有 1 边,(t) 到另一端的路径上没有 3 边

“ 没有 3 边 ” 可以维护关于 1 边和 2 边的并查集,记为 (T) 。 “ (s) 到一端只有 1 边 ” 可以维护关于 1 边的并查集,记为 (O)

那么 (s) 可以到达 (t) 需要满足以下三条之一:

  1. (s)(t)(T) 中同属一个集合。

  2. (s) 所在的 (O) 集合和 (t) 所在的 (T) 集合之间仅有一条 3 边连接

(top_S(u)) 表示 (u) 所在的 (S) 集合中最高的点,那么第二个条件要么是 (top_O(s)) 的父亲与 (t)(T) 中同属一个集合,要么是 (top_T(t)) 的父亲与 (s)(O) 中同属一个集合

于是我们就可以解决第一个询问。

第二个问题中,我们同样可以转化为 (s) 所在的 (T) 集合的大小加上与 (s) 所在 (O) 集合仅有一条 3 边连接的所有 (T) 集合的大小的和。

第一个很好维护。但是第二个信息,由于相邻的 3 边可能很多,所以我们不能直接查询。

但是我们可以先处理一部分信息。在有根的情况下,最多有 1 条朝向根的 3 边。这一条我们可以单独统计。而其他的边都是朝向儿子的,因此我们可以直接维护这些边的贡献和,也就是维护所有与 (O) 相邻,且在子树内的 3 边连接的 (T) 集合的大小之和

这样修改询问都可以做到 (O(1)) 。时间就是 (O(nlog_2n)) 或者 (O(nalpha(n))) 的。

小结:

本题中对于相邻块信息的统计比较常用,其大体思路都是拆分成子树和父亲两个部分,并维护子树信息而查询父亲信息。这样的方法在[CF487E]Tourists也有运用。

代码

#include <map>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;

const int MAXN = 3e5 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && 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;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
void swapp( _T &x, _T &y )
{
	_T t = x; x = y, y = t;
}

int dep[MAXN];
int N;

struct UFS
{
	int fa[MAXN] = {}, big[MAXN] = {}, si[MAXN] = {};
	void MakeSet( const int n ) { for( int i = 1 ; i <= n ; i ++ ) si[i] = 1, fa[i] = i; }
	int FindSet( const int u ) { return fa[u] = ( fa[u] == u ? u : FindSet( fa[u] ) ); }
	int GetSiz( const int u ) { return si[FindSet( u )]; }

	void UnionSet( int u, int v )
	{
		u = FindSet( u ), v = FindSet( v );
		if( u == v ) return ; 
		fa[u] = v, si[v] += si[u];
	}
};

UFS One, OneTwo;

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

int key[MAXN];
int head[MAXN], c[MAXN], siz[MAXN], faE[MAXN], fath[MAXN];
int M, cnt = 1;

LL Id( const int u, const int v )
{ return 1ll * u * N + v; }

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

void DFS( const int u, const int fa )
{
	dep[u] = dep[fa] + 1, fath[u] = fa;
	for( int i = head[u], v ; i ; i = Graph[i].nxt )
		if( ( v = Graph[i].to ) ^ fa )
			DFS( v, u ), key[v] = c[i >> 1];
}

void Init()
{
	One.MakeSet( N ), OneTwo.MakeSet( N );
	DFS( 1, 0 );
	for( int i = 2 ; i <= N ; i ++ )
	{
		if( key[i] == 0 )
			One.UnionSet( i, fath[i] ),
			OneTwo.UnionSet( i, fath[i] );
		if( key[i] == 1 )
			OneTwo.UnionSet( i, fath[i] );
	}
	for( int i = 2 ; i <= N ; i ++ )
		if( key[i] == 2 )
			siz[One.FindSet( fath[i] )] += OneTwo.GetSiz( i );
}

int main()
{
	read( N ), read( M );
	for( int i = 1, a, b ; i < N ; i ++ )
		read( a ), read( b ), read( c[i] ), 
		AddEdge( a, b ), AddEdge( b, a ), c[i] --;
	Init();

	int A, B, S, T, t, otS, otT;
	while( M -- )
	{
		read( A ), read( B ), read( S ), read( T );

		if( dep[A] > dep[B] ) swapp( A, B );
		if( key[B] )
		{
			if( key[B] == 2 ) 
			{
				t = OneTwo.si[B];
				siz[One.FindSet( fath[OneTwo.FindSet( A )] )] += t;
				siz[One.FindSet( A )] -= t;
				OneTwo.UnionSet( B, A );
			}
			if( key[B] == 1 ) 
				siz[One.FindSet( A )] += siz[B], One.UnionSet( B, A );
			key[B] --;
		}

		t = One.FindSet( S ), otS = OneTwo.FindSet( S ), otT = OneTwo.FindSet( T );
		write( otS == otT || t == One.FindSet( fath[otT] ) || OneTwo.FindSet( fath[t] ) == otT ), putchar( ' ' ); 
		int ans = OneTwo.si[otS] + siz[t];
		if( key[t] == 2 ) ans += OneTwo.GetSiz( fath[t] );
		write( ans ), putchar( '
' );
	}
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/13776706.html