树形DP,重复点处理
题目信息:
题目连接 24+26+(34×)
大意: 给你一棵树和一个数
d
d
d ,定义一个集合
S
S
S,
S
S
S 满足
1.
1.
1.非空
2.
2.
2.集合内的点可以直接或者间接相连
3.
3.
3.集合内的最大值
m
a
x
max
max与其它的点值差小于
d
d
d。问有多少个这样区间
S
S
S。
解题思路:
考虑最大值确定的情况,显然可以 d f s dfs dfs找出最大的一个满足情况的集合。这个集合显然也是一个树,根节点就是这个区间的最大值。问题就缩小为,在一棵树中,所选点都能到达根节点的选择方案数。考虑一个点的情况,显然有 选,不选 两种可能。考虑一个点有两个儿子的情况,在父节点 选 的情况下,可选方案数为儿子种类数相乘,父节点 不选 有一种可能。即 d p [ i ] = ∏ j i s o n d p [ j ] + 1 dp[i] =\prod_{j}^{ison}dp[j]+1 dp[i]=∏jisondp[j]+1 。
20min刚了一发,样例都没过…
重要补充:在DP的时候,显然会存在你DP的集合中有与根节点大小相同的点。这些点同样也会被当成根节点再次DP。这样先然会出现重复计算的过程,解决方法就是限制想要 d f s dfs dfs与根节点相同大小的点上时,要求必须该点的编号大于根节点。
奇怪的思路:dfs序,然后枚举所有区间。看似是对的,所有区间之间的点都是连续的。但是当两个点为兄弟关系的时候,dfs序是相邻的,但其实不相邻。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2500;
const ll mod = 1e9+7;
struct node{
int en,v;
}e[maxn<<2];
int ha[maxn],cnt;
ll mp[maxn],d;
int vis[maxn];
int n;
queue<int> q;
void ade(int a,int b){
e[++cnt].v=b;e[cnt].en=ha[a];ha[a]=cnt;
}
ll tre_dp(int x,int rt){
int flag = 0;
vis[x] = rt;
ll an = 1;
for(int i=ha[x];i;i=e[i].en){
int v=e[i].v;
if(vis[v]==rt||mp[rt]<mp[v]||mp[rt]-mp[v]>d) continue;
if(mp[v]==mp[rt]&&v<rt) continue;
an = an*tre_dp(v,rt)%mod;
flag = 1;
}
return flag?(an+1)%mod:2;
}
int main(){
scanf("%lld%d",&d,&n);
for(int i=1;i<=n;++i) scanf("%lld",&mp[i]);
int a,b;
for(int i=1;i<n;++i){
scanf("%d%d",&a,&b);
ade(a,b);ade(b,a);
}
ll ans = 0;
for(int i=1;i<=n;++i){
ans=(ans+tre_dp(i,i)+mod-1)%mod;
}
printf("%lld\n",ans);
return 0;
}