4-10 578 views
这是找树的重心的经典题目。
题目大意:在一棵树中找一个点,删掉以后使所有子树中最大的节点数最少,问应该删哪个点以及最大子树的节点数。
思路:树形dp,f[u]保存以u为根节点的子树的节点数,那么只要比较答案和f[v]+1和n-f[u]-1的关系就可以了。
dfs一遍,重点在f[u]+=dfs1(v)+w;更新f[u],bl=max(bl,f[v]+1);和bl=max(bl,n–f[u]–1);更新bl。
树的重心有下面几条常见性质:
定义1:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。
定义2:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。
性质1:树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
性质2:把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
性质3:把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; struct node{ int v,w; }; vector <node>adj[20010]; int n,ans,bans; int f[20010]; bool vis[20010]; int dfs1(int u) { vis[u]=true; f[u]=0; int bl=0; for(int i=0; i<adj[u].size(); ++i) { int v = adj[u][i].v; int w = adj[u][i].w; if(vis[v]) continue; f[u]+=dfs1(v)+w; bl=max(bl,f[v]+1); } bl=max(bl,n-f[u]-1); if (bl<bans) { bans=bl; ans=u; } return f[u]; } int main() { int w; scanf("%d",&w); while (w--) { scanf("%d",&n); for(int i=1; i<=n; ++i) adj[i].clear(); for(int u=2; u<=n; ++u) { int v, w; scanf("%d%d", &v, &w); adj[v].push_back((node){w, 1}); adj[w].push_back((node){v, 1}); } memset(f,0,sizeof(f)); memset(vis,false,sizeof(vis)); ans=bans=0x3f3f3f3f; dfs1(1); printf("%d %d\n",ans,bans); } return 0; }
- Ceph客户端缓存一致性分析 - 2019年6月21日
- CephFS:什么是CAPS? - 2019年6月21日
- Ceph集群快速搭建教程 - 2019年6月20日