1. 程式人生 > 實用技巧 >[CSP2019]樹的重心

[CSP2019]樹的重心

題目

點這裡看題目。

分析

原來資料的奇怪結尾就可以拿來判斷特徵呀

40pts ~ 55pts

太簡單就不說了。

75pts

考慮完全二叉樹怎麼做。

這裡需要注意一點,就是:\(n=262143=2^{18}-1\) ,也就是說,資料實際上就是一棵滿二叉樹

由於滿二叉樹具有極強的對稱性,我們不難想到這樣解決:

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

考慮根的左子樹上深度為 \(d\) (根的深度為 \(0\) )的節點,我們可以任選一個,計算出拆掉它和它父親之間的邊後,父親那顆子樹的貢獻\(t\)。那麼答案就應該加上 \(2^{d-1}\times t\)

。右子樹同理。

這樣計算就是 \(O(n\log_2n)\) 的。

這個部分還可以推出通項公式。該方法的依據是:

1 . 每個點拆掉了它和它父親之後,它就是自己那部分的重心。

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

3 . 拆掉一個葉子和它父親連的邊,根一定是重心。

然後就可以利用上述依據 \(O(n)\) 計算答案了。

100pts

為了方便,以下設 \(siz_u\) 表示 \(u\) 的子樹的大小。

首先我們需要非常重要的一點:

對於在一個大小為 \(n\) 的樹上的點 \(u\) ,若 \(n-siz_u\le \lfloor\frac n 2\rfloor\)

,且它不是重心,那麼重心一定在 \(u\) 的重子樹內。

看起來很好理解吧?考慮簡單地說明一下。

假如 \(u\)\(k\) 個重兒子,那麼每個重兒子的 \(siz\le \frac {siz_u} k\le \lfloor\frac n 2\rfloor\) 。那麼 \(u\) 就是重心,與假設不符,因而 \(u\) 只有一個重兒子。

同理可以說明, \(u\) 的重兒子的大小 \(>\lfloor\frac n 2\rfloor\) 。也即是說,對於 \(u\) 的非重兒子 \(v\) ,一定有 \(siz_v<\lfloor\frac n 2\rfloor\Rightarrow n - siz_v>\lfloor\frac n 2\rfloor\)

。所以重心一定在 \(u\) 的重兒子裡面。

看看我們得到了什麼?

如果 \(u\) 滿足 \(n - siz_u\le \lfloor \frac n 2\rfloor\) ,那麼:

1 . \(u\) 只有一個重兒子

2 . 要麼 \(u\) 是重心,要麼重心在 \(u\) 的重子樹裡

3 . 如果 \(u\) 是滿足性質的最深的點那麼 \(u\) 就是重心,且 \(u\) 的父親可能是重心

第三條可以結合第二條推出。

也就是說:第一、二條告訴了我們重心的明確方向,第三條告訴了我們重心的判斷標準,並且它還存在單調性

那我們可以幹什麼了?

倍!增!呀!

以上三條直接教會我們,只要我們能處理出 \(u\) 的朝向重兒子的倍增陣列,我們就可以快速地從樹根開始求重心。

這樣我們就可以在知道倍增陣列後 \(O(\log_2n)\) 地求出重心。那麼我們只需要列舉每一條邊,維護倍增陣列並計算重心即可。由於邊原本可能是無序的,因而我們需要進行 DFS ,並中途維護資訊(本質上還是在 “ 換根 ” ),然後就可以倍增出重心了。

時間複雜度是 \(O(n\log_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 );
    //...
}