P10555 [ICPC 2024 Xi'an I] Fix the Tree 题解

· · 题解

直接考虑 O(n) 的做法。

对于每个点 u,删去 u 后形成的每一联通块都要有点被选择,显然选最小是不劣的,可以用换根 DP 解决。

deg_u=d,一共要连 d-1 条边,故还要选 res=d-3 个点,容易证明可以任意选择。

将所以点权扔进桶 cnt 里,从最小非 0 处开始扫,逐次删除,res\Leftarrow res-cnt_i,并将 cnt_i 加到 cnt_{i+1} 里。

因为 res 每次操作至少减 1,所以单点复杂度 O(deg_u)

代码如下: ```cpp #include<bits/stdc++.h> using namespace std; const int N=1000005; vector<int>e[N],p; int w[N],t[N],f[N],g[N],fa[N],n,mn=N,mn2=N; void dfs(const int k) { f[k]=w[k]; int mn=w[k],mn2=w[k]; for(int i:e[k]) if(i!=fa[k]) { fa[i]=k,dfs(i),f[k]=min(f[k],f[i]); if(f[i]<mn) mn2=mn,mn=f[i]; else if(f[i]<mn2) mn2=f[i]; } for(int i:e[k]) if(i!=fa[k]) if(f[i]==mn) g[i]=mn2; else g[i]=mn; } void dfs2(const int k) { for(int i:e[k]) if(i!=fa[k]) g[i]=min(g[i],g[k]),dfs2(i); } void calc(const int k) { if(p.size()==1) return cout<<0<<' ',void(); int st=mn; long long ans=0,s=0,d=p.size()-2; if(w[k]==mn) st=mn2; --t[w[k]]; for(int i:p) ans+=i,--t[i],++t[i+1]; for(int i=st;d;++i) { if(d>t[i]+s) s+=t[i],d-=s,ans+=s*i; else { ans+=d*i; break; } } for(int i:p) ++t[i],--t[i+1]; ++t[w[k]]; cout<<ans<<' '; } int main() { ios::sync_with_stdio(0),cin.tie(0); cin>>n; int u,v; for(int i=1; i<=n; ++i) { cin>>w[i],++t[w[i]]; if(w[i]<mn) mn2=mn,mn=w[i]; else if(w[i]<mn2) mn2=w[i]; } for(int i=1; i<n; ++i) cin>>u>>v,e[u].push_back(v),e[v].push_back(u); dfs(1); g[1]=n+1,dfs2(1); for(int i:e[1]) p.push_back(f[i]); calc(1); for(int i=2;i<=n;++i) { p.clear(); for(int j:e[i]) if(j!=fa[i]) p.push_back(f[j]); p.push_back(g[i]); calc(i); } return 0; } ```