1. 程式人生 > 其它 >Codeforces Global Round 16題解

Codeforces Global Round 16題解

E. Buds Re-hanging

對於這個題該開始還是沒想法的,但這顯然是個思維題,還是要多多動手推樣例,實踐一下。
簡化題意:給定一個有根樹,規定某個點為樹幹,當且僅當這個點不是根,且這個點至少有一個兒子,這個點的所有兒子都是葉子節點。每次可以將一個樹幹及其子樹分離出來,然後將這個點與樹上其他點相連。問:進行若干次操作之後,整個樹的最少的葉子結點的個數?
首先我思考的是:我們這個樹幹從原來的父親節點摘下來後要放到哪裡,可以發現如果練到一個非葉子節點上的話,總的葉子數量是不會減少的,甚至有可能增多(因為原本樹幹的父親可能成為新的葉子)。所以最優的情況一定是將這個樹幹連到一個葉子節點上。可以發現對於任意一個樹的結構我們都可以從葉子節點往上,依次將他們拆離,也就是每次都將當前所有的樹幹摘離,那麼最後只有兩種結果,根節點不能被拆離,只剩下根節點一個點,根節點也能被摘離,整棵樹都能被拆離。那麼對於當前一個點x而言,我們要如何操作,才能使得整個的葉子節點最少。對於x的所有兒子y,我們期望的最優結果就是所有兒子都能夠摘離除去,放到其中一個兒子身上,類似一個鏈的形狀。這樣的話,葉子節點最少。考慮這樣我們需要兒子的那些資訊,如何統計答案?首先需要兒子能不能被完全摘離,還有兒子內部經過調整之後的最少的葉子節點。起初令\(ans[x]=\sum ans[y]\)

,之後,若有一個兒子能夠完全被摘離,那\(ans[x]--\),因為我們可以將y這個兒子及其子樹全部摘離,放到另一個子樹上,減少一個葉子結點。但有一種情況比較特殊,就是當全部的兒子都能被摘離時,我們要將\(ans[x]++\),因為我們至少留一個兒子放上面。那麼考慮x是否能被摘離,若x的全部兒子都能被摘離,則x不能被摘離,否則x就能被摘離。直接樹形DP即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,f[N]; 
vector<int>son[N];
inline bool dfs(int x,int fa)//bool表示能不能被摘離. 
{
	int children=0,cnt=0;//cnt表示能被摘離的兒子的個數。 
	for(auto y:son[x])
	{
		if(y==fa) continue;
		children++;
		if(dfs(y,x)) cnt++; 
		f[x]+=f[y];
	}
	f[x]-=cnt;
	if(cnt==children) f[x]++;
	if(children==0) {f[x]=1;return 0;}
	return (cnt==children)?0:1;
}
int main()
{
//	freopen("1.in","r",stdin);
	int T;cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i=1;i<=n;++i) son[i].clear();
		for(int i=1;i<n;++i) 
		{
			int x,y;cin>>x>>y;
			son[x].push_back(y);
			son[y].push_back(x);
		}
		memset(f,0,sizeof(f));
		dfs(1,0);
		cout<<f[1]<<endl;
	}
	return 0;
}