[USACO22DEC] Barn Tree S

题目描述

Farmer John 的农场有 个牛棚 ,编号为 。有 条道路,每条道路连接两个牛棚,并且从任一牛棚均可通过一些道路到达任一其他牛棚。目前,第 个牛棚中有 个干草捆

为使他的奶牛们满意,Farmer John 想移动这些干草,使得每个牛棚都有相同数量的干草捆。他可以选择任何一对由一条道路连接的牛棚,并命令他的农场工人将不超过第一个牛棚中干草捆数量的任意正整数个干草捆从第一个牛棚移动到第二个牛棚。

请求出一个 Farmer John 可以发出的命令序列,以尽可能少的命令数完成这一任务。输入保证存在符合要求的命令序列。

输入格式

输入的第一行包含整数

第二行包含空格分隔的整数 ,其中

最后 行每行包含两个空格分隔的牛棚编号 ,表示有一条双向道路连接

输出格式

输出命令的最小数量,然后输出该数量的命令序列,每行输出一条命令。

每条命令的格式应为三个空格分隔的正整数:出发牛棚,目标牛棚,以及从出发牛棚移动到目标牛棚的干草捆数量。

如果有多组解,输出任意一组。

样例 #1

样例输入 #1

1
2
3
4
5
4
2 1 4 5
1 2
2 3
2 4

样例输出 #1

1
2
3
4
3
3 2 1
4 2 2
2 1 1

提示

样例 1 解释

在这个例子中,共有十二个干草捆和四个牛棚,这意味着每个牛棚最后必须有三个干草捆。输出样例中的命令顺序可以用以下的自然语言表述:

  1. 从牛棚 到牛棚 ,移动 个干草捆。
  2. 从牛棚 到牛棚 ,移动 个干草捆。
  3. 从牛棚 到牛棚 ,移动 个干草捆。

测试点性质

  • 测试点 满足
  • 测试点 满足
  • 测试点 没有额外限制。

题解

引理

  1. 先可以保证若树边的两端的平均数等于最终每个节点上干草捆数量,那么这条边在解决方案中一定没有用到。(读者自证)
  2. 若该边在解决方案中用过,可以保证仅用过一次。换句话说一定有一种先后次序使得构造方案成立。

构造

  1. 先向下遍历,解决每一个子树问题。
  2. 子树问题按照一定先后次序处理,先处理子树满足整体平均值大于全部平均值,同时将多余的草垛移到父节点。后处理子树满足整体平均值小于全部平均值,将父节点的多余草垛拿走。
  3. 处理即可(详情见代码)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#define int long long
using namespace std;
int n;
int h[5000005] ,sum;
vector<int> e[5000005];
int dp[5000005] ,s[5000005] ,siz[5000005];
struct abc{
int u ,v ,sum;
}ans[5000005];
int cnt;
void dfs(int u ,int fa){//统计每个子树的信息
s[u]=h[u];
siz[u]=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa)
continue;
dfs(v ,u);
s[u]+=s[v];
siz[u]+=siz[v];
dp[u]+=dp[v]+(s[v]!=sum*siz[v]);
}
}
void dfs2(int u ,int fa){//解决方案
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa)
continue;
if(s[v]>siz[v]*sum){
dfs2(v ,u);
ans[++cnt].u=v ,ans[cnt].v=u ,ans[cnt].sum=s[v]-siz[v]*sum;
}
if(s[v]==siz[v]*sum){
dfs2(v ,u);
}
}
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa)
continue;
if(s[v]<siz[v]*sum){
ans[++cnt].u=u ,ans[cnt].v=v ,ans[cnt].sum=siz[v]*sum-s[v];
dfs2(v ,u);
}
}
}
main()
{
cin >> n;
for(int i=1;i<=n;i++){
cin >> h[i];
sum+=h[i];
}
sum/=n;
for(int i=1;i<n;i++){
int u ,v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1 ,1);
dfs2(1 ,1);
if(cnt!=dp[1]){
return 114514;
}
cout << cnt << endl;
for(int i=1;i<=cnt;i++)
cout << ans[i].u << ' ' << ans[i].v << ' ' << ans[i].sum << endl;
}