Codeforces Round #392 (Div. 2)E. Broken Tree

题目大意:

       对于一棵树有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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值