1405 树的距离之和(51nod)
题面:给定一棵无根树,假设它有n个节点,节点编号从1到n, 求1-n这n个节点,到其他n-1个节点的距离之和。
传送门
输入:
第一行包含一个正整数n (n <= 100000),表示节点个数。
后面(n - 1)行,每行两个整数表示树的边。
输出:
每行一个整数,第i(i = 1,2,…n)行表示所有节点到第i个点的距离之和。
这题其实挺水的 但还是想了一会
蒟蒻就用了递推,感觉好像第二个dfs有点dp的味道 emmm
思路:一看就不能暴力。因为是个无根树,所以随便拿一个点来当根都行。(本人比较习惯用1)开个数组size记录每个节点和它子节点集合的大小,开个sum用来记录dfs过程中从根节点到子节点的距离,也就是距离前缀和,f用来记录每个节点到其他所有节点的距离。
第一次:dfs我们可以从根节点向下求根节点到每个子节点的前缀和,用sum记录然后我们把每个节点的sum给了f[1]就求出根节点到所有节点的距离和了。同时回溯时,求出每个节点子集的大小,用size记录。
第二次dfs:已知根节点1到其他节点的距离和,然后还已知每个节点子集的大小,就可以用这玩意求出来f[j]=f[i]+n-2*size[j];
对于遍历到的任意一个节点 i,对于与之相邻的节点 j 来说,答案贡献由 i 到 j 转移首先减小了 size[j]
同时增加了 (n−size[j])(除了size[j]以外的点)因此可以直接得到状态转移方程f[j]=f[i]+n−size[j]∗2。

如图,如果根转移到节点2,距离首先要减少size[2]*1,然后要增加(n-size[2])*1,因为根变为节点2,节点一到节点二子集的所有距离要消失,也就是红色部分(size[v]),要增加从节点2到节点一子集的距离也就是绿色部分(n-size[v])
话不多说,上代码
#include<bits/stdc++.h>
#define cn getchar
#define ll long long
using namespace std;
void in(int &x){
int f1=1;char ch=cn();x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f1=-1;ch=cn();}
while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=cn();}
x*=f1;
}
int n;
struct node{
int x,y,next;
}p[210000];//链表
int len;
ll sum[110000];//记录根节点到子节点的距离
int size[110000];//子树和自己的个数(包括自己)
ll f[110000];//到其他节点的距离和
int last[110000];
void ins(int x,int y){//建边
p[++len]={x,y,last[x]};last[x]=len;
p[++len]={y,x,last[y]};last[y]=len;
}
void dfs1(int x,int fa){
size[x]=1;
for(int i=last[x];i;i=p[i].next){
int y=p[i].y;
if(y==fa)continue;
sum[y]=sum[x]+1;
f[1]+=sum[y];
dfs1(y,x);
size[x]+=size[y];
}
}
void dfs2(int x,int fa){
for(int i=last[x];i;i=p[i].next){
int y=p[i].y;
if(y==fa)continue;
f[y]=f[x]+n-2*size[y];
dfs2(y,x);
}
}
int main(){
in(n);
for(int i=1;i<n;i++){
int x,y;
in(x);in(y);
ins(x,y);
}
dfs1(1,1);
dfs2(1,1);
for(int i=1;i<=n;i++)printf("%lld\n",f[i]);
return 0;
}
给定一棵无根树,求所有节点到其他节点的距离之和。采用两次DFS,第一次计算根节点到所有节点的距离和及子节点大小,第二次根据子节点大小计算每个节点到所有节点的距离和。状态转移方程为f[j]=f[i]+n-size[j]*2。
&spm=1001.2101.3001.5002&articleId=118608686&d=1&t=3&u=368b4d194fdb4cc195850f044f00b45f)
1024

被折叠的 条评论
为什么被折叠?



