首页 > 其他分享 >CF1188A2

CF1188A2

时间:2022-10-16 19:22:24浏览次数:38  
标签:rt ch val int pb fa CF1188A2

首先题目中每条边权值互不相同,于是 A1 的结论依旧适用:有解当且仅当树中不存在度数为 \(2\) 的点。

必要性很好证,度数为二的点不会是叶子,那么每条经过该点修改的路径一定同时经过该点的两条边,所以这两条边增减一致,但初始权值不同,显然无解。

充分性通过如下构造说明。

现在树中任意一非叶子节点至少度数为 \(3\),我们令某一叶子作为根,那么非叶子节点就至少有两棵子树,我们任取点 \(i\) 两棵不同子树中的叶子节点,记作 \(ls_i,rs_i\)。

于是我们可以通过以下方式,给任意点 \(i\) 到 \(root\) 的路径上同时增减一个偶数权值 \(v\):

  • 若 \(i\) 本身就是叶子,直接操作 \((root,i,v)\)。
  • 否则操作 \((root,ls_i,\frac{v}{2}),(root,rs_i,\frac{v}{2}),(ls_i,rs_i,-\frac{v}{2})\)。

实现的时候就 DFS 一遍,在回溯时把 \(i\) 到父亲边上的权值变成 \(0\) 就好了。

时间复杂度 \(\mathcal O(n^2)\)。

具体细节看代码。

Code:

#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
typedef pair <int, int> pii;
const int N = 1005;
int n, rt;
int val[N][N];
vector <int> g[N];
int ch[N][2], fa[N];
vector <pair <pii, int> > ans;

void upd(int x, int v) {
	if (!v) return;
	if (g[x].size() != 1) {
		int w = v >> 1;
		ans.pb(mp(pii(rt, ch[x][0]), w)), ans.pb(mp(pii(rt, ch[x][1]), w)), ans.pb(mp(pii(ch[x][0], ch[x][1]), -w));
	}
	else ans.pb(mp(pii(rt, x), v));
	while (x != rt) val[fa[x]][x] -= v, val[x][fa[x]] -= v, x = fa[x];
}

void dfs(int u, int Fa) {
	fa[u] = Fa;
	if (g[u].size() == 1) ch[u][0] = u;
	for (int v : g[u]) if (v != Fa)
		dfs(v, u), ch[u][ch[u][0] > 0] = ch[v][0];
}

void solve(int u) {
	for (int v : g[u]) if (v != fa[u]) solve(v);
	if (u != rt) upd(u, val[u][fa[u]]);
}

int main() {
	scanf("%d", &n);
	for (int i = 1, u, v, w; i < n; ++i) scanf("%d%d%d", &u, &v, &w), val[u][v] = val[v][u] = w, g[u].pb(v), g[v].pb(u);
	for (int i = 1; i <= n; ++i) if (g[i].size() == 2) return printf("NO"), 0;
	printf("YES\n");
	for (int i = 1; i <= n; ++i) if (g[i].size() == 1) { rt = i; break; }
	dfs(rt, 0), solve(rt);
	printf("%d\n", ans.size());
	for (auto i : ans) printf("%d %d %d\n", i.fi.fi, i.fi.se, i.se);
	return 0;
}

标签:rt,ch,val,int,pb,fa,CF1188A2
From: https://www.cnblogs.com/Kobe303/p/16796838.html

相关文章