【樹形dp】連通塊計數
阿新 • • 發佈:2019-01-05
題目描述
給出一棵n個點的樹,每個點有一個權值a。從這棵樹上選出一個點集,使得選出的點連通,且滿足點集中的點的權值最大值與最小值之差不超過k,問有多少種選點集的辦法。 兩種選點集的辦法不同當且僅當點集中的點的標號不同。
輸入
第一行,包含兩個整數n,k。 第二行,包含n個整數a 1, a 2, · · · , a n。 接下來n − 1行,每行包含兩個正整數u, v,表示u, v兩點間有一條邊。
輸出
僅輸出一行,包含一個數,表示選點集的辦法。 這個數可能很大,輸出時對998244353取模。
樣例輸入
(如果複製到控制檯無換行,可以先貼上到文字編輯器,再複製)
4 1
2 1 3 2
1 2
1 3
3 4
樣例輸出
8
提示
對於100%的資料,0 ≤ n, k, a i ≤ 2000。題解
我們可以以每一個節點為樹根,然後對整棵樹進行計數。 設f[i]表示以i為根的子樹中符合題意的聯通塊的個數,則 f[i]= ∏(j ∈ s(i)) f[j] 其中s(i)表示i的兒子的集合 -----------------------------------------------------------------------------------------------------------------------------------------------------#include<cstdio>
#include<vector>
using namespace std;
const int mn = 2005, mod = 998244353;
int w[mn], k, g[mn][mn], p[mn];
int dp(int u, int f, int x)
{
int ret = 1;
for(int i = 1, v; v = g[u][i]; ++i)
if(v != f && w[x] >= w[v] && w[x] - w[v] <= k && (x < v || w[x] != w[v]))
ret = 1ll * ret * (dp(v, u, x) + 1) % mod;
return ret;
}
int main()
{
int n, a, b, ans = 0;
int i;
scanf("%d%d", &n, &k);
for(i = 1; i <= n; i++)
scanf("%d", &w[i]);
for(i = 1; i < n; i++)
{
scanf("%d%d", &a, &b);
g[a][++p[a]] = b, g[b][++p[b]] = a;
}
for(i = 1; i <= n; i++)
ans = (ans + dp(i, 0, i)) % mod;
printf("%d", ans);
}