題目大意
每次在每個聯通塊中選一個點 \(u\),刪除這個點使得聯通塊分成若干個聯通塊,再從聯通塊中選點 \(v\),在新樹上連線 \(u,v\),求新樹葉節點的最大個數。
分析
易轉化為求原樹的最大獨立集,設 \(f_{u,0/1}\) 為以 1 為根時不選/選 \(u\) 的最大獨立集。顯然有:
\[dp_{u,0}=\sum\limits_vmax(dp_{v,0/1})\\ dp_{u,1}=\sum\limits_vdp_{v,0}+1
\]
手模第一個樣例發現如果以原樹的葉子為根節點時,可以同時選擇相鄰的點,要求以各節點為根的最大獨立集,考慮換根 dp,設 \(g_{u,0/1}\) 為以 \(u\) 為根,不選/選 \(u\) 的最大獨立集。顯然有
\[g_{u,0}=max(g_{fa_u,1},f_{u,0}+g_{fa_u,0}-max(f_{u,0},f_{u,1})\\
g_{u,1}=f_{u,1}+g_{fa_u,0}-max(f_{u,0},f_{u,1})\]
程式碼
void dfs(int u,int par){
f[u][0]=0,f[u][1]=1;
for(int v:G[u]){
if(v==par) continue;
dfs(v,u);
f[u][0]+=max(f[v][1],f[v][0]);
f[u][1]+=f[v][0];
}
}
void DFS(int u,int par){
ans=max(ans,max(g[u][1],g[u][0]+(G[u].size()==1)));
for(int v:G[u]){
if(v==par)
continue;
g[v][0]=max(g[u][1],f[v][0]+(g[u][0]-max(f[v][0],f[v][1])));
g[v][1]=f[v][1]+(g[u][0]-max(f[v][0],f[v][1]));
DFS(v,u);
}
}
void Main(){
n=rd;
for(int i=1;i<n;i++){
int u=rd,v=rd;
G[u].pb(v),G[v].pb(u);
}
dfs(1,0);
g[1][0]=f[1][0],g[1][1]=f[1][1];
DFS(1,0);
cout<<ans<<endl;
ans=0;
for(int i=1;i<=n;i++) G[i].clear();
}