前言
话说自己觉得区间 d p \tt dp dp学的还行,但是做题速度慢了些,也不完全是学得太慢的原因,效率也很重要。希望学树形 d p \tt dp dp这个版块时能百尺竿头更进一步。
题目
话说数据范围是 n , d < = 2000 n,d<=2000 n,d<=2000
思路
考虑树形 d p \tt dp dp,令 d p [ i ] dp[i] dp[i] 为 i i i 为根节点合法子树个数。
很显然,如果只这样处理状态进行树形 d p \tt dp dp,会有重复状态。这时候,我们选择对 d p dp dp 进行一些限制:我们认为,若枚举的根为 R o o t Root Root,那么 V a l [ R o o t ] Val[Root] Val[Root] 是子树中权值最大的点(最小的点也可以,根本目的是去重)。然而,这样也会有重复的状态——即有权值和 R o o t Root Root 相同的点。因此再进一步限制:若权值相同,则 R o o t Root Root 是编号最大的一个点即可。
如此,便可以直接做树形 d p \tt dp dp的转移: d p [ R o o t ] = ∑ d p [ S o n ] ∗ d p [ R o o t ] dp[Root]=\sum dp[Son]*dp[Root] dp[Root]=∑dp[Son]∗dp[Root]
注意权值的判断和 d d d 的限制——洛谷上的翻译有问题,应该是小于等于
时间复杂度 O ( n 2 ) O(n^2) O(n2)
Code
LL a[MAXN], d, n, dp[MAXN];
vector<LL> G[MAXN];
void Dfs(LL x,LL Pre,LL Root)
{
dp[x] = 1;
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (to == Pre)
continue;
if ((a[to] < a[Root] || (a[to] == a[Root] && to <= Root)) && a[Root] - a[to] <= d)
{
Dfs(to, x, Root);
dp[x] += dp[x] * dp[to];
dp[x] %= Mod;
}
}
}
int main()
{
read( d ); read( n );
for (Int i = 1; i <= n; ++ i)
read( a[i] );
for (Int i = 1; i < n; ++ i)
{
LL u, v;
read( u ); read( v );
G[u].push_back( v );
G[v].push_back( u );
}
LL Ans = 0;
for (Int i = 1; i <= n; ++ i)
Dfs(i, i, i), Ans = (Ans + dp[i]) % Mod;
printf("%lld", Ans);
return 0;
}


475

被折叠的 条评论
为什么被折叠?



