题意:给你一棵树,每个点有一个点权,求最大点-最小点小于等于d的子集个数。
由于复杂度可以支持n方的,所以我们可以通过枚举每一个点,作为根且是里面的最大点,那么我们就可以通过一遍dfs,如果根节点点减去当前点的儿子节点的值<=d,那么由乘法原理可得,当前点的答案可以乘等于当前点儿子节点的答案,即为dp[now]*=(dp[to[i]]+1),由于可以不选,所以还要+1种不选的情况。
但是,当有两个相同值的节点时,答案会重复计算,所以我们可以通过巧妙地定向来解决重复问题,即要求如果当前走到的节点与根节点权值相同,那么我们规定当且仅当当前走到的节点编号小于根结点的编号我们才走,这样子,其他权值相同节点的答案就不会重复计算了。
下附AC代码。
#include<stdio.h>
#include<string.h>
#define maxn 2005
typedef long long ll;
const ll mod=1e9+7;
int n,d,tot;
ll dp[maxn];
int val[maxn];
int head[maxn<<1],nex[maxn<<1],to[maxn<<1];
void add(int x,int y)
{
to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
void dfs(int now,int fa,int root)
{
dp[now]=1;
for(int i=head[now];i;i=nex[i])
{
if(to[i]!=fa)
{
if(val[root]>=val[to[i]] && val[root]-val[to[i]]<=d)
{
if(val[root]==val[to[i]] && to[i]<root) continue;
dfs(to[i],now,root);
dp[now]*=(dp[to[i]]+1);
dp[now]%=mod;
}
}
}
}
int main()
{
scanf("%d%d",&d,&n);
for(int i=1;i<=n;i++)
scanf("%d",&val[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
dfs(i,-1,i);
ans+=dp[i];
ans%=mod;
}
printf("%I64d\n",ans);
}