Acwing287.积蓄程度 树形dp换根

link
二次扫描与换根,对于没有确定根的题目往往可以用到,可以减少一个枚举根的复杂度

题意

在这里插入图片描述

思路

1.考虑朴素解法,枚举根 s s s ,设 d[x] 为从 x 出发流向子树的最大流量,很容易求出dp方程(即代码中dfs部分)。对于每个根都进行一次dp,时间复杂度 O ( n 2 ) O(n^2) O(n2)
2.考虑优化,首先任选一个源点 s s s 进行同上的dp,设 f[x] 表示把 x 作为根,流向整个水系的最大流量。则对于根节点 x x x , f[x] = d[x]。考虑 x x x 的子节点 y y yf[y] 包涵两部分:

  1. y y y 流向以 y y y 为根的子树的流量,即为 d[y]
  2. y y y 沿着父节点 x x x 的河道,流向水系中其他部分的流量,设为ad

容易得到 f[y] = f[x] + ad,那么怎么求 ad 呢?
把 x 作为根节点的总流量为 f[x], 从 x 流向 y 的流量为 min(d[y], w[x,y]) ,其中 w[x,y] 表示 xy 两点间河道容量。那么 x 流向除 y 以外的其他部分流量就是二者之差,于是把y作为根,先流到 x ,再流向其他部分的流量就是这个差与 w[x,y] 取最小值的结果。(前提是 x , y x, y x,y 度数均大于1)

if(in[cur] != 1 && in[to] != 1)
	ad = min(f[cur] - min(e[i].dis, d[to]), e[i].dis);
else if(in[cur] != 1 && in[to] == 1) 
	ad = min(f[cur] - e[i].dis, e[i].dis);
else
	ad = e[i].dis;

注意 dfs2(to, cur) 的搜索要放到更新完 to 之后。

代码

int n;
int head[maxn], cnt;
struct Edge {
	int to, dis, next;
}e[maxn * 2];
bool vis[maxn];
int d[maxn], f[maxn];
int in[maxn];
void add(int u, int v, int w) {
	++cnt;
	e[cnt].to = v;
	e[cnt].dis = w;
	e[cnt].next = head[u];
	head[u] = cnt;
}
int ans;
void dfs(int cur = 1, int fa = 0) {
	for(int i = head[cur]; i; i = e[i].next) {
		int to = e[i].to;
		if(to == fa) continue;
		dfs(to, cur);
		if(in[to] == 1) {
			d[cur] += e[i].dis;
		}
		else {
			d[cur] += min(e[i].dis, d[to]);
		}
	}
}
void dfs2(int cur = 1, int fa = 0) {
	for(int i = head[cur]; i; i = e[i].next) {
		int to = e[i].to;
		if(to == fa) continue;
		f[to] = d[to];
		int ad;
		if(in[cur] != 1 && in[to] != 1)
			ad = min(f[cur] - min(e[i].dis, d[to]), e[i].dis);
		else if(in[cur] != 1 && in[to] == 1) 
			ad = min(f[cur] - e[i].dis, e[i].dis);
		else
			ad = e[i].dis;
		f[to] += ad;
		ans = max(ans, f[to]);
		dfs2(to, cur);
	}
}
void solve() {
	cnt = 0;
	ans = 0;
	memset(f, 0, sizeof(f));
	memset(d, 0, sizeof(d));
	memset(in, 0, sizeof(in));
	memset(head, 0, sizeof(head));
	cin >> n;
	for(int i = 1; i < n; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		in[u]++;
		in[v]++;
		add(u, v, w);
		add(v, u, w);
	}
	dfs(1, 0);
	f[1] = d[1];
	ans = f[1];
	dfs2(1, 0);
	cout << ans << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值