CF486D Valid Sets

发布时间 2023-11-15 20:59:59作者: Candycar

题目描述:

给定 \(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;
}