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