zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    DNS 截持模拟及环境搭建
    Ant、Gradle、Python三种打包方式的介绍
    oc/c/c++混编老文,写的很好,mark
    好文!关于iOS下的正则表达式实战案例
    Java设计模式——享元模式
    Java 消息机制之回调详解
    windows版爬取csdn
    14.6.2 Configuring InnoDB for Read-Only Operation
    dump iot表
    heap表按字符串和数值型排序规则
  • 原文地址:https://www.cnblogs.com/crashed/p/13776706.html
Copyright © 2011-2022 走看看