這道題我們可以套路的設置f[i]為當前節點為根的滿足條件方案數,然後枚舉根,計算必須包含當前根的方案

但是似乎很難計算

所以我們可以搞一個前綴和,將聯通塊的最大數和最小數相減<=k的方案和<=k-1的方案計算一遍,減去就是答案

則f[i]=(f[son]+1)*… (f[son]滿足條件)

這樣子我們只需要判斷son是否滿足條件,因為son的所有子樹已經判斷過了

但是這樣可能會算重:當有多個節點的值與當前節點的值相同,都會算一遍

於是我們可以硬點只有根標號小的節點才會計算1次,大的直接跳過

代碼:

#include<bits/stdc++.h>
using namespace std;
#define jzm 19260817ll
typedef long long ll;
ll n,k,a[4010],v[8010],nxt[8010],h[4010],ec;
void add(ll x,ll y){v[++ec]=y;nxt[ec]=h[x];h[x]=ec;}
ll dfs(ll x,ll fa,ll p){
	ll ans=1;
	for(ll i=h[x];i;i=nxt[i])
		if(v[i]!=fa&&((a[v[i]]<a[p]||(a[v[i]]==a[p]&&p<v[i]))&&a[p]-a[v[i]]<=k))
			ans=(ans*(dfs(v[i],x,p)+1))%jzm;
	return ans;
}
int main(){
	freopen("lkf.in","r",stdin);
	freopen("lkf.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(ll i=1;i<n;i++){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);add(y,x);
	}
	ll ans1=0;
	for(ll i=1;i<=n;i++)
		ans1=(ans1+dfs(i,0,i))%jzm;
	if(k!=0){
		k--;
		for(ll i=1;i<=n;i++)
			ans1=(ans1-dfs(i,0,i)+jzm)%jzm;
	}
	printf("%lld",ans1);
}
05-28 23:57