题目大意:
对于一棵树有n个点,n-1条边,每条边有4个值x,y,w,p,x是离根进的点,y是另一个点,p和w都是特征值,又定义了broken tree的要求是一条边的p小于它所连子树的所有边的w,要求把给定的树装换成w和最大的非broken树(转换过程中只能减不能加,而且树的的w必定为正整数)
题目解法:
先分析误解问题,如果一棵树本身有存有边的p小于子树w和的话是一定无解的(因为不能加)。
在分析构造w和最大的树的问题,直接构造有点难度,于是我们不妨先把他变成一颗最小的非broken树,然后再尽可能加回来。
那就是分成两布:
第一步:将给定的树尽可能变小且是非broken的树,每次将这条边减少(p-子树w和)和将本身w变成1的费用的最小值(因为w最少是1),如果过程中发现有存在p小于子树w和的一定无解。从叶子向上。
第二步:将之前转化好的树和原来的树对比,在可以的范围内增加,从根向下。记录可以修改的最大值,对于和根相连的子树可以修改的最大值就是和原来的值的差,之后逐层传递,修改能增加的最大值。
最后输出就好了。
代码:
#include "iostream"
#include "cstdio"
#include "math.h"
#include "algorithm"
#include "string"
#include "string.h"
#include "vector"
#include "map"
#include "queue"
using namespace std;
struct Edge {
long long x, y, w, p;
}edge[1000005], ee[1000005];
long long w[1000005], ww[1000005];
int n;
vector<int>G[1000005];
void dfs(int x, int y, int id) {
for (int i = 0;i < G[y].size();i++) {
int nextid = G[y][i];
dfs(edge[nextid].x, edge[nextid].y, nextid);
}
if (id == 0)
return;
if (edge[id].p < w[y]) {
puts("-1");
exit(0);
}
long long temp = min(edge[id].p - w[y], edge[id].w - 1);
edge[id].p -= temp;
edge[id].w -= temp;
w[x] += w[y] + edge[id].w;
}
void dfs(int x, int y, int id, int mm) {
if (id != 0) {
int temp = min((long long)mm, ee[id].p - edge[id].p);
edge[id].p += temp;
edge[id].w += temp;
mm = min((long long)(mm - temp), edge[id].p - w[y]);
ww[id] = temp;
}
for (int i = 0;i < G[y].size();i++) {
int nextid = G[y][i];
if (id == 0)
mm = 2e9;
dfs(edge[nextid].x, edge[nextid].y, nextid, mm);
mm -= ww[nextid];
ww[id] += ww[nextid];
}
}
int main() {
scanf("%d", &n);
for (int i = 1;i < n;i++) {
scanf("%lld %lld %lld %lld", &edge[i].x, &edge[i].y, &edge[i].w, &edge[i].p);
ee[i] = edge[i];
G[edge[i].x].push_back(i);
}
dfs(0, 1, 0);
dfs(0, 1, 0, 2e9);
printf("%d\n", n);
for (int i = 1;i < n;i++) {
printf("%lld %lld %lld %lld\n", edge[i].x, edge[i].y, edge[i].w, edge[i].p);
}
return 0;
}