Codeforces 1188A 构造

题意:给你一颗树,树的边权都是偶数,并且边权各不相同。你可以选择树的两个叶子结点,并且把两个叶子结点之间的路径加上一个值(可以为负数),问是否可以通过这种操作构造出这颗树?如果可以,输出构造方案。初始树的边权都是0。

思路:A1很简单,只要判断是否有度数为2的点就可以了。对于A2, 由于边权各不相同,所以A1的结论同样适用。现在我们来构造一组答案。官方题解的构造方式是这样的:我们假设要让一个节点u到叶子结点v的路径都加上一个值x,并且知道叶子结点l1, l2都可以到达u,我们执行以下操作:v到l1的路径加上x / 2, v到l2的路径加上x / 2, l1 到 l2的路径加上-x / 2,这样除了u到v的路径,其它路径的值没有变(太菜了,想不到。。。)。那么,我们从树根开始,从上到下逐个构造边权即可。

由于n只有1000,所以实现方式有两种。

第一种很暴力,赋值操作直接暴力加,复杂度O(n ^ 2)。

代码:

#include <bits/stdc++.h>
#define pii pair<int, int>
#define LL long long
using namespace std;
const int maxn = 1010;
vector<pii> G[maxn];
vector<int> son[maxn];
LL add[maxn];
struct node {
	int x, y;
	LL z;
};
vector<node> ans;
int root = 1;
int f[maxn];
void adde(int x, int y, int z) {
	G[x].push_back(make_pair(y, z));
	G[y].push_back(make_pair(x, z));
}
 
int dfs(int x, int fa) {
	f[x] = fa;
	for (auto y : G[x]) {
		if(y.first== fa) continue;
		int tmp = dfs(y.first, x);
		son[x].push_back(tmp);
	}
	if(G[x].size() == 1) {
		return x;
	}
	return son[x][0];
}
 
void update(int x, int p, int val) {
	while(x != p) {
		add[x] += val;
		x = f[x];
	}
}
void dfs1(int x, int fa) {
	int cnt = 0;
	if(x == root) {
		int y = G[x][0].first;
		if(G[y].size() == 1) {
			ans.push_back((node){x, y, G[x][0].second});
			return;
		}
		LL tmp = G[x][0].second;
		ans.push_back((node){son[y][0], root, tmp / 2});
		ans.push_back((node){son[y][1], root, tmp / 2});
		ans.push_back(node{son[y][0], son[y][1], -tmp / 2});
		dfs1(y, x);
	} else {
		for (auto y : G[x]) {
			if(y.first == fa) continue;
			LL tmp = y.second - add[y.first];
			int tmp1;
			if(cnt == 0) tmp1 = 1;
			else tmp1 = 0;
			ans.push_back((node){son[x][cnt], root, tmp / 2});
			ans.push_back((node){son[x][cnt], son[x][tmp1], tmp / 2});
			ans.push_back(node{root, son[x][tmp1], -tmp / 2});
			update(son[x][cnt], x, tmp);
			dfs1(y.first, x);
			cnt++;
		}
	}
}
 
int main() {
	int n;
	int x, y, z;
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d%d", &x, &y, &z);
		adde(x, y, z);
	}
	for (int i = 1; i <= n; i++) {
		if(G[i].size() == 2) {
			printf("NO\n");
			return 0;
		}
	}
	for (int i = 1; i <= n; i++) {
		if(G[i].size() == 1) {
			root = i;
			break;
		}
	}
	dfs(root, -1);
	dfs1(root, -1);
	printf("YES\n");
	printf("%d\n", ans.size());
	for (int i = 0; i < ans.size(); i++) {
		printf("%d %d %lld\n", ans[i].x, ans[i].y, ans[i].z);
	}
}

第二种用了类似树剖中重儿子的思想,我们给一颗子树中决定一个优先级最高的叶子结点,这样加的操作是这个叶子结点到它的祖先的路径上进行的,其它的路径没有影响,这样累加影响的时候,如果这个叶子结点,把前面的影响累加上,否则不加。复杂度O(n)。

代码:

#include <bits/stdc++.h>
#define pii pair<int, int>
#define LL long long
using namespace std;
const int maxn = 1010;
vector<pii> G[maxn];
vector<int> son[maxn];
LL add[maxn];
struct node {
	int x, y;
	LL z;
};
vector<node> ans;
int root = 1;
int v[maxn];
void adde(int x, int y, int z) {
	G[x].push_back(make_pair(y, z));
	G[y].push_back(make_pair(x, z));
}

int dfs(int x, int fa) {
	for (auto y : G[x]) {
		if(y.first== fa) continue;
		int tmp = dfs(y.first, x);
		son[x].push_back(tmp);
	}
	if(G[x].size() == 1) {
		v[x] = x;
		return x;
	}
	v[x] = son[x][0];
	return son[x][0];
}

void dfs1(int x, int fa, int tot) {
	int cnt = 0;
	if(x == root) {
		int y = G[x][0].first;
		if(G[y].size() == 1) {
			ans.push_back((node){x, y, G[x][0].second});
			return;
		}
		LL tmp = G[x][0].second;
		ans.push_back((node){son[y][0], root, tmp / 2});
		ans.push_back((node){son[y][1], root, tmp / 2});
		ans.push_back(node{son[y][0], son[y][1], -tmp / 2});
		dfs1(y, x, 0);
	} else {
		for (auto y : G[x]) {
			if(y.first == fa) continue;
			LL tmp = y.second;
			if(v[y.first] == v[x]) tmp -= tot;
			int tmp1;
			if(cnt == 0) tmp1 = 1;
			else tmp1 = 0;
			ans.push_back((node){son[x][cnt], root, tmp / 2});
			ans.push_back((node){son[x][cnt], son[x][tmp1], tmp / 2});
			ans.push_back(node{root, son[x][tmp1], -tmp / 2});
			dfs1(y.first, x, y.second);
			cnt++;
		}
	}
}

int main() {
	int n;
	int x, y, z;
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d%d", &x, &y, &z);
		adde(x, y, z);
	}
	for (int i = 1; i <= n; i++) {
		if(G[i].size() == 2) {
			printf("NO\n");
			return 0;
		}
	}
	for (int i = 1; i <= n; i++) {
		if(G[i].size() == 1) {
			root = i;
			break;
		}
	}
	dfs(root, -1);
	dfs1(root, -1, 0);
	printf("YES\n");
	printf("%d\n", ans.size());
	for (int i = 0; i < ans.size(); i++) {
		printf("%d %d %lld\n", ans[i].x, ans[i].y, ans[i].z);
	}
}

两份代码中为了实现方便,都找了一个度为1的点为根。

原文地址:https://www.cnblogs.com/pkgunboat/p/11145961.html

时间: 2024-11-08 11:20:15

Codeforces 1188A 构造的相关文章

B - Save the problem! CodeForces - 867B 构造题

B - Save the problem! CodeForces - 867B 这个题目还是很简单的,很明显是一个构造题,但是早训的时候脑子有点糊涂,想到了用1 2 来构造, 但是去算这个数的时候算错了... 用1 2 来构造 可以先枚举一些数来找找规律. 1 1 2 2 3 1 1 1    2 1 1 4 .... 可以发现每一个数都是 n/2+1 的可能, 所以反过来推过去就是 (s-1)*2  或者(s-1)*2+1 这个(s-1)*2+1的答案才是正确答案 因为 这个s可以==1 #i

Johnny Solving CodeForces - 1103C (构造,图论)

大意: 无向图, 无重边自环, 每个点度数>=3, 要求完成下面任意一个任务 找一条结点数不少于n/k的简单路径 找k个简单环, 每个环结点数小于n/k, 且不为3的倍数, 且每个环有一个特殊点$x$, $x$只属于这一个环 任选一棵生成树, 若高度>=n/k, 直接完成任务1, 否则对于叶子数一定不少于k, 而叶子反向边数>=2, 一定可以构造出一个环 #include <iostream> #include <algorithm> #include <c

The minimal unique substring CodeForces - 1159D (构造)

核心观察是形如01,001,0001,...的串循环时, $n$每增长1, $k$就增长1. #include <iostream> #include <sstream> #include <algorithm> #include <cstdio> #include <math.h> #include <set> #include <map> #include <queue> #include <stri

codeforces 的20道C题

A - Warrior and Archer CodeForces - 595C n  偶数  然后n个数字 A B 轮流取一个 A让差变小B让差变大 直到最后2 个   求的是最小剩下的差 最后剩下的 L R  相距 n/2    求一下最小的就行 #include <iostream> #include <cstdio> #include <cmath> #include <map> #include <algorithm> #include

[训练日志] 7月17-21日

codeforces 351C [构造一个n*m的括号序列,每个位置根据%n的情况(和)分别有花费,求最小费用] [考虑一般dp.有结论状态不会超过n.所以我们以n位为一块,用f[i][k][j]表示目前i位,前缀和最小为k,当前和为j的最小费用,预处理出从状态转移矩阵T.然后用min_plus矩阵加速T^m计算.] [状态不超过2n的证明:在刚到达2n的前一刻的前缀和一定>n,所以可以将之后和为负的块往前调整,使得状态不超过2n] [此题也可考虑倍增, F[k][i][j]表示2^k * n个

Codeforces Gym 100187K K. Perpetuum Mobile 构造

K. Perpetuum Mobile Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100187/problem/K Description The world famous scientist Innokentiy almost finished the creation of perpetuum mobile. Its main part is the energy generator whic

codeforces 487C Prefix Product Sequence (模逆元+构造)

转自http://blog.csdn.net/houserabbit/article/details/41513745 题解写的真棒.. 题目链接:http://codeforces.com/problemset/problem/487/C 题目大意:构造一个1~n的排列  使得n个前缀积对n取余是一个0~n-1的排列 题目分析:好题,首先我们通过简单的分析可以得到n肯定是最后一个数,因为如果n在前面,前缀积肯定不止1个是n的倍数,也就是说对n取模至少有两个0,显然不满足排列,也就是说取模得到排

Codeforces Round #275 (Div. 1)A. Diverse Permutation 构造

Codeforces Round #275 (Div. 1)A. Diverse Permutation Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/482/problem/A Description Permutation p is an ordered set of integers p1,   p2,   ...,   pn, consisting of n distinct posi

Codeforces 453C Little Pony and Summer Sun Celebration(构造)

题目链接:Codeforces 453 Little Pony and Summer Sun Celebration 题目大意:n个节点,m条边,然后m行给定边,最后一行表示每个节点需要进过的次数为奇数次还是偶数次. 解题思路:构造,任意从一个奇数点开始(统一森林的处理),然后每次向下遍历没有经过的节点,并且回溯,每次回溯都要判断一下刚才走过的点满不满足条件,不满足的话就再走一次.最后对于根节点,最后一次回溯要不要走根据当前走过的次数决定. #include <cstdio> #include