CF1188A2
首先题目中每条边权值互不相同,于是 A1 的结论依旧适用:有解当且仅当树中不存在度数为 \(2\) 的点。
必要性很好证,度数为二的点不会是叶子,那么每条经过该点修改的路径一定同时经过该点的两条边,所以这两条边增减一致,但初始权值不同,显然无解。
充分性通过如下构造说明。
现在树中任意一非叶子节点至少度数为 \(3\),我们令某一叶子作为根,那么非叶子节点就至少有两棵子树,我们任取点 \(i\) 两棵不同子树中的叶子节点,记作 \(ls_i,rs_i\)。
于是我们可以通过以下方式,给任意点 \(i\) 到 \(root\) 的路径上同时增减一个偶数权值 \(v\):
- 若 \(i\) 本身就是叶子,直接操作 \((root,i,v)\)。
- 否则操作 \((root,ls_i,\dfrac{v}{2}),(root,rs_i,\dfrac{v}{2}),(ls_i,rs_i,-\dfrac{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;
}