题意

给定一个 nn 个节点的有点权的树,求满足块中最大值与最小值之差小于等于 dd 的联通块数。

n2000,d2000,ai2000n\le2000,d\le2000,a_i\le2000

思路

不难想到枚举每个点作为最小值的答案,以选定的点为根进行 dp。

定义 dpidp_i 表示以 ii 为根的子树包含 ii 点的联通块数量,那么只需要考虑点权在 (art,art+d](a_rt,a_rt+d] 之间与点权等于 arta_rt 但是编号小于 rtrt(这一步为了去重)的点。

我们令不满足上述要求的店 dpdp 值为零,则有转移方程:

dpi=(dpson+1)dp_i=\prod (dp_son+1)

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
#define int long long
#define chmax(x, y) x = max(x, y)
#define chmin(x, y) x = min(x, y)
#define SP << " " <<
#define fish signed
using namespace std;
mt19937 rnd(1209);
const int INF = 0x3f3f3f3f3f3f3f3f, mod = 1e9 + 7;
const int N = 2e3 + 10;
vector<int> e[N];
int dp[N], n, d, now, a[N];
int ans;
void dfs(int u, int fa)
{
dp[u] = 1;
for(auto v : e[u])
{
if(v == fa) continue;
dfs(v, u);
if(dp[v]) dp[u] = dp[u] * (dp[v] + 1) % mod;
}
if(a[u] > a[now] + d || a[u] < a[now] || a[u] == a[now] && u > now) dp[u] = 0;
}
fish main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> d >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1, u, v; i < n; i ++) cin >> u >> v, e[u].push_back(v), e[v].push_back(u);
for(int i = 1; i <= n; i ++) now = i, dfs(i, 0), ans = (dp[i] + ans) % mod;
cout << ans << "\n";
return 0;
};