题意: 一棵树树上节点都有一个值ai,都条边的长度为1,现在知道每个节点的代价值
为 all 节点t与它的距离即t节点的值积的和pi,问所有的ai的值.(0<=ai<=1000,n<=250000)
*以前一直觉得是道Guass消元题,但是n有点大,系数矩阵都不能存,更不用说O(n^3)的时间复杂度。今天看了,感觉跟以前做过的树题很相似,细细推导,发觉是道子树的和的技巧题。
*不妨记为root为1的有根树,那么sum[i]表示根为节点i的子树节点值a的和。
因为v为根的子树上的所有节点(包括v)距离u的dist比距离v的dist多1,而非v子树上的点距离u的dist比距离v的dist少1,所有v的代价比u的代价多一颗子树上的值和sum[v],而少非子树上的值和(sum[1]-sum[v])。
所以我们需要做一次DFS,将所有p[son]-fa[fa]之和求出,然后联立第一个公式:得到
sum[1]即解出,在做一个dfs,解出sum[v],然后减去所有回溯的子树权值和,得到了a[v].
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define llt long long
using namespace std;
const int Size=25*1e4+10;
/*
子树和 技巧题
以rt为根的子树上所有节点的值的和为 sum[rt]
所有
*/
vector <int> V[Size];
llt p[Size],sum[Size]={0},a[Size];//sum[1] 表示整棵以1为根的树节点总和;
//求出a1+a2+……+an 先找出所有节点代价与父节点的代价差
llt DFS(int rt,int fa){
llt tot=0;
for(int i=0;i<V[rt].size();++i){
int son=V[rt][i];
if(fa==son) continue;
tot+=(p[son]-p[rt])+DFS(son,rt);
}
return tot;
}
llt dfs(int rt,int fa){
if(rt!=1) sum[rt]=(sum[1]+p[fa]-p[rt])/2;
a[rt]=sum[rt];
for(int i=0;i<V[rt].size();++i){
if(V[rt][i]==fa) continue;
a[rt]-=dfs(V[rt][i],rt);
}
return sum[rt];
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
V[u].push_back(v);
V[v].push_back(u);
}
for(int i=1;i<=n;++i)
scanf("%lld",&p[i]);
if(n==1){cout<<0<<endl;return 0;}
sum[1]=(2*p[1]+DFS(1,0))/(n-1);
dfs(1,0);
printf("%lld",a[1]);
for(int i=2;i<=n;++i)
printf(" %lld",a[i]);
printf("\n");
return 0;
}