Description
SJY有一天被LLT紧急召去计算一些可能的损失。LLT元首管理的SHB国的交通形成了一棵树,现在将会出现一颗陨石砸在SHB国中,并且陨石砸毁的必定是SHB国构成的交通树上的一条路径。SHB国的损失可表示为被砸毁的路径上的所有城市价值之积。现在还暂时无法确定陨石的掉落路线,所以LLT元首希望SJY能够告诉他SHB国在受到每一种砸毁方式后会受到的损失之和模10086之后的值。注意:单独一个节点也被认为是合法的路径。
Input
第1行一个数n,表示城市数。
第2行n个数,第i个数表示第i个城市的价值。
第3到n+1行,每行两个数u,v,表示城市u,v之间有一条道路。
Output
包含一个数,表示SHB国将受到的损失之和。
Sample Input
5
7 6 6 1 1
1 2
2 3
2 4
1 5
Sample Output
778
Data Constraint
对于20%的数据,n<=100;
对于50%的数据,n<=3000;
对于100%的数据,n<=100000。
//written by zzy
题目大意:
求一个树的所有路径积(路径上的所有点的权值之积)
题解:
树形dp,设
f
[
x
]
f[x]
f[x]为以
x
x
x为根的子树的到
x
x
x的所有路径积之和(所有直链到x的路径积),
考虑如何转移和求出答案
发现转移很简单,
f
[
x
]
=
s
u
m
w
[
x
]
∗
f
[
s
o
n
]
f[x]=sum{ w[x]*f[son]}
f[x]=sumw[x]∗f[son]
但
f
[
x
]
f[x]
f[x]不能算上折链(“V”形路径)的路径积,(因为
f
[
x
]
f[x]
f[x]要层层传上去,就不能表示两点之间的路径积了)
所以答案要再另外加上所有折链的路径积,
考虑如何求,
其实折链可以看成两条直链(从一个节点到
x
x
x再到另一个节点),
子树
x
x
x到子树
y
y
y的所有路径积
(
x
,
y
)
(x,y)
(x,y)等于
f
[
x
]
∗
w
[
l
c
a
(
x
,
y
)
]
∗
f
[
y
]
f[x]*w[lca(x,y)]*f[y]
f[x]∗w[lca(x,y)]∗f[y]
但不能两两计算
(
x
,
y
)
(x,y)
(x,y),(时间复杂度会退化)
可以考虑当遍历完一个子树后将其加入
f
[
x
]
f[x]
f[x]中,
累记起来(相当于不断合并子树,边合并边更新答案)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 100005
#define Mod 10086
using namespace std;
int i,j,n,m,l,x;
long long ans;
int w[N],f[N];
bool vis[N];
int list[N],v[2*N],next[2*N];
void add(int x) {
next[++l]=list[x];
list[x]=l;
}
void dfs(int x) {
f[x]=w[x];
for (int t=list[x],y=v[t];t;t=next[t],y=v[t])
if (!vis[y]) {
vis[y]=true; dfs(y);
ans=(ans+(f[x]*f[y])%Mod)%Mod;//更新答案
f[x]=(f[x]+(f[y]*w[x])%Mod)%Mod;//合并子树
}
}
int main()
{
scanf("%d",&n);
for (i=1;i<=n;i++) {
scanf("%d",&w[i]);
ans+=w[i];
}
for (i=1;i<=n-1;i++) {
scanf("%d%d",&x,&v[i*2-1]); add(x);
v[i*2]=x; add(v[i*2-1]);
}
vis[1]=true;
dfs(1);
printf("%d",ans);
}