题目描述:
给定 \(n\) 个点的树,点有点权,求满足最大点权与最小点权之差小于等于 \(d\) 的连通子图数目。答案对 \(10^9 + 7\) 取模。
数据范围:
\(1\le d\le 2000,1\le n\le 2000\)
\(1\le a_i\le 2000\)
\(1\le u,v\le n\)
思路:
根据我们以往的做题经验,因为要求选出的是一个连通子图,则我们考虑以一些点为根然后顺着根往下选一些边,这样这个子图一定是联通的。
关键是要以哪些点为根?如果以每个点为根的话,很明显会有很多重复的情况,而且不好去重。所以我们并不知道以哪些点为根。
但是!题目中还有一个限制:要求 \(mx-mn\le d\) 所以我们不妨钦定一个点为整个联通子图中的最大值,然后以这个点为根,令根节点的值为 \(a_{rt}\)。
这样做我们就不用枚举最大最小值了,直接判断是否满足 \(a_{rt}-a_u\le d\)
这个题目还有一个注意点:他有可能有相同的点权,所以会造成重复的统计。所以考虑钦定只有编号小于 \(rt\) 的点才会被统计,这样就可以保证不重不漏。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2005;
const int mod=1e9+7;
int d,n;
int a[maxn];
vector<int>G[maxn];
int dp[maxn];
bool check(int u,int v){
return a[u]>a[v]||(a[u]==a[v]&&u>v);
}
void dfs(int u,int f,int p){
dp[u]=1;
for(int v:G[u]){
if(v==f)continue;
if(!check(p,v)||a[p]-a[v]>d)continue;
dfs(v,u,p);
dp[u]=(dp[u]*(dp[v]+1))%mod;
}
}
signed main(){
cin>>d>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
int ans=0;
for(int i=1;i<=n;i++){
dfs(i,0,i);
ans=(ans+dp[i])%mod;
}
cout<<ans<<endl;
return 0;
}
标签:le,int,子图,点为,CF486D,Valid,maxn,Sets,dp
From: https://www.cnblogs.com/Candycar/p/17834757.html