P8900
[USACO22DEC] Barn Tree S
题目描述
Farmer John 的农场有 \(N\) 个牛棚 \((2 \le N \le 2 \times 10^5)\),编号为 \(1 \cdots N\)。有 \(N−1\) 条道路,每条道路连接两个牛棚,并且从任一牛棚均可通过一些道路到达任一其他牛棚。目前,第 \(j\) 个牛棚中有 \(h_j\) 个干草捆 \((1 \le h_j \le 10^9)\)。
为使他的奶牛们满意,Farmer John 想移动这些干草,使得每个牛棚都有相同数量的干草捆。他可以选择任何一对由一条道路连接的牛棚,并命令他的农场工人将不超过第一个牛棚中干草捆数量的任意正整数个干草捆从第一个牛棚移动到第二个牛棚。
请求出一个 Farmer John 可以发出的命令序列,以尽可能少的命令数完成这一任务。输入保证存在符合要求的命令序列。
- 测试点 \(2-8\) 满足 \(N \le 5000\)。
- 测试点 \(7-10\) 满足 \(v_i=u_i+1\)。
- 测试点 \(11-16\) 没有额外限制。
这是一个树上构造,所以我们考虑以 1 为根,按照拓扑序构造。从拓扑序入手貌似是很常见的切入口。从菊花图和链入手也是很常见的出发点。
菊花图怎么办,多的给根,少的让根给即可。注意顺序。
链怎么办?从链底开始一步一步向上调整即可。
考虑树的情况。
妈的一开始的想法园不来了,真的菜啊啊啊啊啊啊啊啊啊。
从上往下构造。
对于点 \(u\),考虑给每个子树分配多少。
- 子树内多了,就把子树先处理好,再把儿子多的拿过来
- 子树内少了,就给子树分配再处理子树。
为啥从下到上原不来呢?不知道不知道不知道不知道
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5;
struct piar {
int ind, val;
bool operator < (const piar &other) const {
return val < other.val;
}
};
struct node {
int fr, to, w;
node(int F, int T, int W) {
fr = F, to = T, w = W;
}
};
vector <node> ans;
vector <int> gra[N + 10];
int n, aim, sum = 0;
int fat[N + 10], a[N + 10], siz[N + 10], asiz[N + 10];
void getsiz(int u, int fa) {
siz[u] = 1, asiz[u] = a[u];
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa) continue;
getsiz(v, u);
asiz[u] += asiz[v];
siz[u] += siz[v];
}
}
void dfs(int u, int fa) {
fat[u] = fa;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa) continue;
int w = siz[v] * (aim);
if(w < asiz[v]) {
dfs(v, u);
ans.push_back(node(v, u, - w + asiz[v]));
a[u] += (asiz[v] - w);
a[v] -= (asiz[v] - w);
// cout << a[v] << ' ' << aim << endl;
}
else if(w == asiz[v]) dfs(v, u);
}
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
if(v == fa) continue;
int w = siz[v] * (aim);
if(w > asiz[v]) {
ans.push_back(node(u, v, - asiz[v] + w));
a[v] += (w - asiz[v]);
a[u] -= (w - asiz[v]);
// cout << a[v] << ' ' << aim << endl;
dfs(v, u);
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin >> n;
sum = 0;
for(int i = 1; i <= n; i++)
cin >> a[i], sum += a[i];
aim = sum / n;
for(int i = 1, x, y; i < n; i++) {
cin >> x >> y;
gra[x].push_back(y);
gra[y].push_back(x);
}
getsiz(1, 0);
dfs(1, 0);
cout << ans.size() << endl;
for(int i = 0; i < ans.size(); i++)
cout << ans[i].fr << ' ' << ans[i].to << ' ' << ans[i].w << endl;
}