zoukankan      html  css  js  c++  java
  • [CSP2019]树的重心

    题目

    点这里看题目。

    分析

    原来数据的奇怪结尾就可以拿来判断特征呀

    40pts ~ 55pts

    太简单就不说了。

    75pts

    考虑完全二叉树怎么做。

    这里需要注意一点,就是:(n=262143=2^{18}-1) ,也就是说,数据实际上就是一棵满二叉树

    由于满二叉树具有极强的对称性,我们不难想到这样解决:

    首先,答案一定包含 (frac{n(n+1)}{2}-rt),其中 (rt) 是树的根。

    考虑根的左子树上深度为 (d) (根的深度为 (0) )的节点,我们可以任选一个,计算出拆掉它和它父亲之间的边后,父亲那颗子树的贡献(t)。那么答案就应该加上 (2^{d-1} imes t) 。右子树同理。

    这样计算就是 (O(nlog_2n)) 的。

    这个部分还可以推出通项公式。该方法的依据是:

    1 . 每个点拆掉了它和它父亲之后,它就是自己那部分的重心。

    2 . 拆掉左边的任何边,根的右儿子一定是重心;拆掉右边,左儿子一定是重心。

    3 . 拆掉一个叶子和它父亲连的边,根一定是重心。

    然后就可以利用上述依据 (O(n)) 计算答案了。

    100pts

    为了方便,以下设 (siz_u) 表示 (u) 的子树的大小。

    首先我们需要非常重要的一点:

    对于在一个大小为 (n) 的树上的点 (u) ,若 (n-siz_ule lfloorfrac n 2 floor) ,且它不是重心,那么重心一定在 (u) 的重子树内。

    看起来很好理解吧?考虑简单地说明一下。

    假如 (u)(k) 个重儿子,那么每个重儿子的 (sizle frac {siz_u} kle lfloorfrac n 2 floor) 。那么 (u) 就是重心,与假设不符,因而 (u) 只有一个重儿子。

    同理可以说明, (u) 的重儿子的大小 (>lfloorfrac n 2 floor) 。也即是说,对于 (u) 的非重儿子 (v) ,一定有 (siz_v<lfloorfrac n 2 floorRightarrow n - siz_v>lfloorfrac n 2 floor) 。所以重心一定在 (u) 的重儿子里面。

    看看我们得到了什么?

    如果 (u) 满足 (n - siz_ule lfloor frac n 2 floor) ,那么:

    1 . (u) 只有一个重儿子

    2 . 要么 (u) 是重心,要么重心在 (u) 的重子树里

    3 . 如果 (u) 是满足性质的最深的点那么 (u) 就是重心,且 (u) 的父亲可能是重心

    第三条可以结合第二条推出。

    也就是说:第一、二条告诉了我们重心的明确方向,第三条告诉了我们重心的判断标准,并且它还存在单调性

    那我们可以干什么了?

    倍!增!呀!

    以上三条直接教会我们,只要我们能处理出 (u) 的朝向重儿子的倍增数组,我们就可以快速地从树根开始求重心。

    这样我们就可以在知道倍增数组后 (O(log_2n)) 地求出重心。那么我们只需要枚举每一条边,维护倍增数组并计算重心即可。由于边原本可能是无序的,因而我们需要进行 DFS ,并中途维护信息(本质上还是在 “ 换根 ” ),然后就可以倍增出重心了。

    时间复杂度是 (O(nlog_2n))

    代码

    完全二叉树部分分

    int p[MAXN], pos[MAXN];
    int head[MAXN], deg[MAXN];
    
    void DFS( const int u, const int fa )
    {
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    		{
    			if( p[pos[u] << 1] )
    				p[pos[v] = pos[u] << 1 | 1] = v;
    			else 
    				p[pos[v] = pos[u] << 1] = v;
    			DFS( v, u );
    		}
    }
    
    int main()
    {
        //...
        for( int i = 1 ; i <= N ; i ++ )
            if( deg[i] == 2 )
            {
                p[pos[i] = 1] = i;
                DFS( i, 0 );
                break;
            }
        LL ans = 1ll * N * ( N + 1 ) / 2 - p[1];
        ans += 1ll * N / 2 * ( p[2] + p[3] );
        ans += 1ll * ( N / 2 + 1 ) * p[1];
        //...
    }
    

    100pts

    typedef long long LL;
    
    const int MAXN = 3e5 + 5, MAXLOG = 20;
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    int jump[MAXN][MAXLOG];
    int hs[MAXN], phs[MAXN], fath[MAXN], siz[MAXN];
    LL ans;
    
    void addEdge( const int from, const int to )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	head[from] = cnt;
    }
    
    void init( const int u )
    {
    	for( int i = 1 ; i <= lg2 ; i ++ )
    		jump[u][i] = jump[jump[u][i - 1]][i - 1];
    }
    
    void DFS( const int u, const int fa )
    {
    	fath[u] = fa;
    	siz[u] = 1, hs[u] = phs[u] = 0;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    		{
    			DFS( v, u ), siz[u] += siz[v];
    			if( siz[hs[u]] < siz[v] ) phs[u] = hs[u], hs[u] = v;
    			else if( siz[phs[u]] < siz[v] ) phs[u] = v;
    		}
    	jump[u][0] = hs[u];
    	init( u );
    }
    
    bool chk( const int u, const int all )
    {
    	return MAX( siz[hs[u]], all - siz[u] ) <= all >> 1;
    }
    
    void calc( const int u, const int fa )
    {
    	int ths = hs[u], tsiz = siz[u], p; 
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    		{
    			if( hs[u] == v ) hs[u] = phs[u];
    			if( siz[fa] > siz[hs[u]] ) hs[u] = fa;
    			siz[u] = N - siz[v];
    			jump[u][0] = hs[u];
    			init( u );
    			
    			p = u;
    			for( int i = lg2 ; ~ i ; i -- )
    				if( jump[p][i] && siz[u] - siz[jump[p][i]] <= siz[u] >> 1 )
    					p = jump[p][i];
    			ans += chk( p, siz[u] ) * p + chk( fath[p], siz[u] ) * fath[p];
    			
    			p = v;
    			for( int i = lg2 ; ~ i ; i -- )
    				if( jump[p][i] && siz[v] - siz[jump[p][i]] <= siz[v] >> 1 )
    					p = jump[p][i];
    			ans += chk( p, siz[v] ) * p + chk( fath[p], siz[v] ) * fath[p];
    			
    			fath[u] = v;
    			calc( v, u );
    			
    			jump[u][0] = hs[u] = ths, siz[u] = tsiz, fath[u] = fa;
    			init( u );
    		}
    }
    
    void init()
    {
    	cnt = ans = 0;
    	for( int i = 1 ; i <= N ; i ++ )
    		head[i] = 0;
    }
    
    int main()
    {
        //...
    	lg2 = log2( N ), init();
    	for( int i = 1, a, b ; i < N ; i ++ )
        	read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
    	DFS( 1, 0 );
    	calc( 1, 0 );
        //...
    }
    
  • 相关阅读:
    HDFS集群优化篇
    JavaScript基础知识-流程控制之if语句
    JavaScript基础知识-代码块
    Linux下查看某个进程打开的文件数-losf工具常用参数介绍
    Linux下查看线程数的几种方法汇总
    Linux网卡调优篇-禁用ipv6与优化socket缓冲区大小
    UGUI Text(Label)
    Vuforia Android 6 Camera Error
    轻量级C#编辑器RoslynPad((基于Roslyn编译器))
    [AR]Vumark(下一代条形码)
  • 原文地址:https://www.cnblogs.com/crashed/p/13374135.html
Copyright © 2011-2022 走看看