[CF486D] Valid Sets

前言

话说自己觉得区间 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值