牛客练习赛55 - E:树
给定一颗边权全为 1 的树,求两两点之间距离的平方的和
如果是求两两之间距离的和,直接树形dp算每条边的贡献即可;现在题目要求距离平方的和,还是考虑树形dp枚举每条边计算每条边的贡献,套路地将平方展开看一下,比如算: (a+b+c)*(a+b+c) = a^2 + a*(b+c) + b^2 + b*(a+c) + c^2 + c*(a+b),发现对于每一条边它的贡献都是一样的计算方式,边权又为 1,那么每条边的贡献就是经过这条边的所有路径的长度和,这个只需要维护子树节点的个数和子树节点到根的距离和就能 O(1) 快速计算,树形dp枚举每条边就做完了
#include
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 1e6+16;
const int mod = 998244353;
inline int read(){
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){if(c == '-')f=-1; c=getchar();}
while(isdigit(c)) {x = x*10+c-'0'; c = getchar();}
return x * f;
}
inline void write(int x){
if (x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int head[maxn],tot,x,y,n;
struct edge{
int to,nxt;
}e[maxn<<1];
inline void add(int u,int v){
e[++tot] = (edge){v,head[u]};
head[u] = tot;
}
LL sz[maxn],sum[maxn],ans;
void dfs1(int x,int fa){
sz[x] = 1;
for(int i = head[x];i>0;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs1(v,x); sz[x] += sz[v];
sum[x] = (sum[x]+sum[v]+sz[v])%mod;
}
}
void dfs2(int x,int fa,LL cnt,LL len){
for(int i = head[x];i>0;i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
LL newcnt = (cnt+sz[x]-sz[v]+mod)%mod;
LL newlen = ((len+sum[x]-sum[v]-sz[v])%mod+mod)%mod;
ans += newcnt*sz[v]+newlen*sz[v]+sum[v]*newcnt;
ans %= mod;
dfs2(v,x,newcnt,(newlen+newcnt)%mod);
}
}
int main(){
n = read();
for(int i = 1;i < n; ++i){
x = read(), y = read();
add(x,y); add(y,x);
}
dfs1(1,0); dfs2(1,0,0,0);
write(ans*2%mod);
return 0;
}