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,設爲以爲根的子樹的到的所有路徑積之和(所有直鏈到x的路徑積),
考慮如何轉移和求出答案
發現轉移很簡單,
但不能算上折鏈(“V”形路徑)的路徑積,(因爲要層層傳上去,就不能表示兩點之間的路徑積了)
所以答案要再另外加上所有折鏈的路徑積,
考慮如何求,
其實折鏈可以看成兩條直鏈(從一個節點到再到另一個節點),
子樹到子樹的所有路徑積等於
但不能兩兩計算,(時間複雜度會退化)
可以考慮當遍歷完一個子樹後將其加入中,
累記起來(相當於不斷合併子樹,邊合併邊更新答案)
#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);
}