题解:CF1156D 0-1-Tree

一种换根 dp 的做法,但是是一种和题解区不同的做法。

由于这种树上路径的问题若假定一个点为根会更加的好求,所以我们可以枚举 xxx,然后假定 xxx 为根节点,再去找有多少个满足条件的 yyy

显然有对于每个 xxx 复杂度为 O(n)O(n)O(n) 的搜索,记录 u,valu,valu,val 分别表示目前的点的编号 uuu,以及 xxxuuu 走过的最大权值 valvalval,记录 valvalval 的原因是需要根据目前的路径最大权值选择可以走过的边,例如 valvalval111 的时候就不可以经过权值为 000 的边了。答案显然就是每一个 xxx 可以到达的 uuu 的数量。复杂度为 O(n2)O(n^2)O(n2),需要优化。

发现 (u,val)(u,val)(u,val) 的总个数就只有 2×n2 \times n2×n 个,如果使用记忆化搜索的话可以把时间复杂度优化到 O(n)O(n)O(n)。记录 dpu,valdp_{u,val}dpu,val 表示从根节点 xxx 走到 uuu 时,路径最大权值为 valvalval 时,uuu 可以到达的点的个数。

但是这样显然不可行,在不同的 xxx 下面这个值可以是不同的。

于是我们可以先跑出来根节点为 111 的所有 dp 的值,然后再跑换根 dp,这个时候已经和前面提到的搜索没多大关系了。

前面的树形 dp 的转移方程很好想,就是对于每一个 uuu 枚举可以到的点 vvv 并得到两点之间的边的权值 www,如果 w=0w=0w=0,就是 dpu,0+dpv,0+1→dpu,0dp_{u,0}+dp_{v,0}+1 \to dp_{u,0}dpu,0+dpv,0+1dpu,0,否则就是 dpu,0+dpv,1+1→dpu,0dp_{u,0}+dp_{v,1}+1 \to dp_{u,0}dpu,0+dpv,1+1dpu,0 并且 dpu,1+dpv,1+1→dpu,1dp_{u,1}+dp_{v,1}+1 \to dp_{u,1}dpu,1+dpv,1+1dpu,1

然后的换根 dp 就是这样的反着来,最后答案显然就是对于每一个根节点 xxx 下面的 dpx,0dp_{x,0}dpx,0 即可。具体可以看代码。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=200010;
vector<pair<int,int> > v[N];
int dp[N][2];//dp数组
int ans=0;
void dfs(int u,int pre){
	for(auto [i,w]:v[u]){
		if(i==pre) continue;
		dfs(i,u);
		if(w==0) dp[u][0]+=dp[i][0]+1;
		else dp[u][0]+=dp[i][1]+1,dp[u][1]+=dp[i][1]+1;//转移方程
	}
}
void change(int u,int i,int w){
	if(w==0) dp[u][0]-=dp[i][0]+1;
	else dp[u][0]-=dp[i][1]+1,dp[u][1]-=dp[i][1]+1;//反着来,先断边再加边
	if(w==0) dp[i][0]+=dp[u][0]+1;
	else dp[i][0]+=dp[u][1]+1,dp[i][1]+=dp[u][1]+1;
}
void dfs2(int u,int pre){
	ans+=dp[u][0];
	for(auto [i,w]:v[u]){
		if(i==pre) continue;
		change(u,i,w);//换根
		dfs2(i,u);
		change(i,u,w);
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<n;i++){
		int x,y,w;
		cin>>x>>y>>w;
		v[x].push_back({y,w});
		v[y].push_back({x,w});
	}
	dfs(1,0);//树形dp
	dfs2(1,0);//换根dp
	cout<<ans;
	return 0;
}

如果对本题题解有疑问,欢迎在讨论区提出,我会尽量给出解答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值