标签:最大值 枚举 std sign 相等 tor 选择 += 转移
给定一棵带点权的树,求满足点权极差不超过 \(d\) 的连通子图的个数。\(n,d \le 2000\)。
枚举每个点作为最大值 \(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;
}
标签:最大值 枚举 std sign 相等 tor 选择 += 转移
原文地址:https://www.cnblogs.com/mollnn/p/13838385.html