#include<iostream> #include<algorithm> #include<cstdio> #include<string> #include<cstring> using namespace std; typedef long long ll; const int N=100010; const int inf=0x3f3f3f3f; int h[N],ne[N],idx; int st[N]; int n; int tot; struct node{ int u; int v; int to; }; node e[2*n]; void add(int a,int b){ e[idx].u=a; e[idx].v=b; e[idx].to=h[a]; h[a]=idx++; } void dfs(int u){ int i; if(st[u]) return ; st[u]=true; tot++; for(i=h[u];i!=-1;i=e[i].to) dfs(e[i].v); } int p,tmp; int dfs1(int u,int fa,int n){ int i; int tot=0; int sum=0; int ans=0; for(i=h[u];i!=-1;i=e[i].to){ int j=e[i].v; if(j==fa) continue; tot=dfs1(j,u,n); ans=max(ans,tot); sum+=tot; } ans=max(ans,n-sum-1); if(ans<tmp){ tmp=ans; p=u; } return sum+1; } ll res; int dfs2(int u,int fa){ int i; int t=0; int sum=0; for(i=h[u];i!=-1;i=e[i].to){ int j=e[i].v; if(j==fa) continue; t=dfs2(j,u); res+=(1ll*t)*(1ll*(n-t)); sum+=t; } return sum+1; } int main(){ scanf("%d",&n); memset(h,-1,sizeof h); int i; int u,v; for(i=0;i<n-2;i++){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } int root1,root2,n1,n2; for(i=1;i<=n;i++){ if(!st[i]){ tot=0; dfs(i); if(i==1){ root1=1; n1=tot; } else{ root2=i; n2=tot; } } } int g1,g2; tmp=inf; dfs1(root1,-1,n1); g1=p; tmp=inf; dfs1(root2,-1,n2); g2=p; add(g1,g2); add(g2,g1); dfs2(1,-1); printf("%lld\n",res); return 0; }
本题是动态规划当中的树形dp问题 本质上是求解树的重心。
通过dfs分别求解两树的重心后相连,在合并的树上求解距离和
距离和表示为每条边左节点和右节点个数的乘积(考虑这条边在每两个点的路径上的贡献度)
树的重心定义为除去该点后联通子树的节点数最大值最小的点。
性质:
1.每个树最多有两个重心,若存在两个重心则两个重心必相邻。
2.两个树合并后的重心在两树重心连线上。