题意:
给定一棵n(2000)个节点的树 每个节点上有个数字 问 有多少棵子树满足树中最大数字与最小数字的差不超过d
思路:
根据数据猜复杂度可能为n^2 想到尝试树形dp
假如枚举现在树中的最大值 那么最小值可以求出 这时不在数值范围内的节点都可以标记掉
那么假设这个最大值的点我一定选取 那么就可以dp出一定选这个点的情况下子树的种类数
假设u是父节点 v是子节点 那么已知dp[u]=dp[u]*dp[v] 特殊的v如果是叶子 那么dp[v]=2 即可选可不选 如果u是不是根 那么dp[u]最后要+1 因为可以不选u
我们发现这样dp还是不可以的 因为假设现在枚举的最大值为10 树中可能有两个10的节点是连通的 这时计数就会出问题 不过没关系 我们刚才的定义是“一定选某个点” 那么假设我们dp算过了“一定选一号10节点”的情况 这时就可以把一号10节点标记成坏节点 下次处理二号10节点的时候就可以一定不选刚才的点了
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 2010
#define mod 1000000007
int n, d, m, big, small;
int val[N], a[N], vis[N];
struct edge {
int v, next;
} ed[N * 2];
int head[N], tot;
LL dp[N], ans;
void add(int u, int v) {
ed[tot].v = v;
ed[tot].next = head[u];
head[u] = tot++;
}
void dfs(int u, int key) {
vis[u] = 1;
dp[u] = 1;
for (int i = head[u]; ~i; i = ed[i].next) {
int v = ed[i].v;
if (!vis[v]) {
dfs(v, 1);
dp[u] = (dp[u] * dp[v]) % mod;
}
}
dp[u] = (dp[u] + key) % mod;
vis[u] = 0;
}
int main() {
scanf("%d%d", &d, &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &val[i]);
a[i] = val[i];
head[i] = -1;
}
tot = 0;
for (int i = 2; i <= n; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
sort(a + 1, a + n + 1);
m = unique(a + 1, a + n + 1) - a - 1;
for (int i = m; i >= 1; i--) {
big = a[i];
small = big - d;
for (int j = 1; j <= n; j++) {
if (val[j] < small || val[j] > big)
vis[j] = 1;
else
vis[j] = 0;
}
for (int j = 1; j <= n; j++) {
if (!vis[j] && val[j] == big) {
dfs(j, 0);
ans = (ans + dp[j]) % mod;
vis[j] = 1;
}
}
//cout << a[i] << " " << ans << endl;
}
cout << ans << endl;
return 0;
}