Living-Dream 系列笔记 第63期
树的中心
当选取树上节点 \(u\) 为根时,最长链最短,则称 \(u\) 为树的中心。
-
性质一:至多 \(2\) 个且一定相邻。
-
性质二:一定位于树的直径上。
-
性质三:若一棵树有多条直径,则它们必定交于树的中心。
-
性质四:树的中心为根时,根到直径两端点分别为最长 / 次长链。
U392706
板子。
code
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int n; struct E{ int v,w; }; vector<E> G[N]; vector<int> ans; int len1[N],len2[N],up[N]; void dfsd(int cur,int fa){ for(auto i:G[cur]){ if(i.v==fa) continue; dfsd(i.v,cur); if(len1[i.v]+i.w>len1[cur]) len2[cur]=len1[cur], len1[cur]=len1[i.v]+i.w; else if(len1[i.v]+i.w>len2[cur]) len2[cur]=len1[i.v]+i.w; } } void dfsu(int cur,int fa){ for(auto i:G[cur]){ if(i.v==fa) continue; up[i.v]=up[cur]+i.w; if(len1[i.v]+i.w!=len1[cur]) up[i.v]=max(up[i.v],len1[cur]+i.w); else up[i.v]=max(up[i.v],len2[cur]+i.w); dfsu(i.v,cur); } } int main(){ cin>>n; for(int i=1,u,v,w;i<n;i++) cin>>u>>v>>w, G[u].push_back({v,w}), G[v].push_back({u,w}); dfsd(1,0),dfsu(1,0); int mini=1e9; for(int i=1;i<=n;i++){ if(mini>max(len1[i],up[i])) mini=max(len1[i],up[i]),ans.clear(),ans.push_back(i); else if(mini==max(len1[i],up[i])) ans.push_back(i); } sort(ans.begin(),ans.end()); for(int i:ans) cout<<i<<'\n'; return 0; }
CF1294F
显然三个点选两个树的直径上的点 \(u,v\) 更优。
至于第三个点 \(w\),我们可以认为 \(w\) 一定会与 \(u \to v\) 的路径交于 \(lca(w,v)\)。
于是枚举 \(w\) 求 LCA 并取最大即可,\(O(n \log n)\)。
当然更简单而高效的方法就是以 \(u \to v\) 上的点进行多起点 BFS 求最长路即可,\(O(n)\)。
这里提供的实现是法二。
code
#include<bits/stdc++.h> using namespace std; const int N=2e5+5; int n; int x,y,dis,pre[N]; int z,ans; vector<int> G[N<<1]; bool vis[N]; struct Edge{ int u,sum; }; void dfs(int cur,int sum){ if(sum>=dis) dis=sum,y=cur; for(int i:G[cur]) if(i!=pre[cur]) pre[i]=cur,dfs(i,sum+1); } void bfs(){ queue<Edge> q; int now=y; //z=pre[y]; while(now) q.push({now,0}),vis[now]=1,now=pre[now]; while(!q.empty()){ Edge cur=q.front(); q.pop(); if(cur.sum>=ans&&cur.u!=x&&cur.u!=y) ans=cur.sum,z=cur.u; for(int i:G[cur.u]) if(!vis[i]) q.push({i,cur.sum+1}),vis[i]=1; } } int main(){ cin>>n; for(int i=1,u,v;i<n;i++) cin>>u>>v, G[u].push_back(v), G[v].push_back(u); pre[1]=0,dfs(1,0); x=y,pre[x]=0,dfs(x,0); bfs(); cout<<dis+ans<<'\n'<<x<<' '<<y<<' '<<z; return 0; }
AT_agc001_c
反向思考,我们要求最少删除的点数,即是求最多保留的点数。
于是我们对于 \(k\) 进行奇偶性分讨,从而构造直径为 \(k\) 的树,在所有这些里面取保留点数最多的即可。
code
#include<bits/stdc++.h> using namespace std; const int N=2e3+5; int n,k,tot,ans; vector<int> G[N<<1]; void dfs(int cur,int fa,int sum){ tot++; if(sum==k/2) return; for(int i:G[cur]) if(i!=fa) dfs(i,cur,sum+1); } int main(){ cin>>n>>k; for(int i=1,u,v;i<n;i++) cin>>u>>v, G[u].push_back(v), G[v].push_back(u); if(k%2==0) for(int i=1;i<=n;i++) tot=0,dfs(i,0,0),ans=max(ans,tot); else for(int i=1;i<=n;i++) for(int j:G[i]) tot=0,dfs(i,j,0),dfs(j,i,0),ans=max(ans,tot); cout<<n-ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】