[CF486D] Valid Sets

Description

给定一棵带点权的树,求满足点权极差不超过 (d) 的连通子图的个数。(n,d le 2000)

Solution

枚举每个点作为最大值 (M),那么此时只有点权 (ge M-d) 的点是可以被考虑的。

树形 dp,设 (f[p]) 表示以当前选择的最大值点为根的情况下,(p) 的子树内,选取一个包含 (p) 的非空连通子图的方案数。

转移时,对于 (p) 的孩子 (q_1,q_2,...,q_k),依次枚举并更新 (f[p]),即 (f[p] leftarrow f[p] + f[p] f[q_i])

注意点权是可以重复的,这样统计会造成重复计算,我们必须要构造点之间的严格全序关系,因此当点权相等时,比较编号大小即可。

#include <bits/stdc++.h>
using namespace std;

#define int long long 
const int N = 2005;
const int mod = 1e+9+7;

vector <int> g[N];

int n,d;
int vis[N];
int val[N];
int f[N];
int maxVertex;  // 枚举的最大点

void dfs(int p)
{
    vis[p]=1;
    if(val[p]==val[maxVertex])
    {
        // 权值相等,p 的序号必须更小
        if(p>maxVertex) return;
    }
    else if(val[p]>val[maxVertex]) 
    {
        return;
    }
    else
    {
        // 权值不等,必须满足限制条件
        if(val[p]<val[maxVertex]-d) return;
    }
    // 设定边界值
    f[p]=1;     // 选自己
    // 处理孩子
    for(int q:g[p])
    {
        if(!vis[q])
        {
            dfs(q);
            f[p]=(f[p]+f[p]*f[q])%mod;
        }
    }
}

signed main()
{
    cin>>d>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>val[i];
    }
    for(int i=1;i<n;i++) 
    {
        int t1,t2;
        cin>>t1>>t2;
        g[t1].push_back(t2);
        g[t2].push_back(t1);
    }
    int ans=0;      // 总答案
    for(int i=1;i<=n;i++)
    {
        // 处理 i 为 maxVertex 的情况
        maxVertex=i;
        fill(f+1,f+n+1,0);
        fill(vis+1,vis+n+1,0);
        dfs(maxVertex);
        ans+=f[maxVertex];
        ans%=mod;
    }
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/mollnn/p/13838385.html