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

Codeforces 1188A 构造的更多相关文章

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

    B - Save the problem! CodeForces - 867B 这个题目还是很简单的,很明显是一个构造题,但是早训的时候脑子有点糊涂,想到了用1 2 来构造, 但是去算这个数的时候算错 ...

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

    大意: 无向图, 无重边自环, 每个点度数>=3, 要求完成下面任意一个任务 找一条结点数不少于n/k的简单路径 找k个简单环, 每个环结点数小于n/k, 且不为3的倍数, 且每个环有一个特殊点 ...

  3. Codeforces 746G(构造)

                                                                                                      G. ...

  4. C - Long Beautiful Integer codeforces 1269C 构造

    题解: 这里的m一定是等于n的,n为数最大为n个9,这n个9一定满足条件,根据题目意思,前k个一定是和原序列前k个相等,因此如果说我们构造出来的大于等于原序列,直接输出就可以了,否则,由于后m-k个一 ...

  5. Dividing the numbers CodeForces - 899C (构造)

    大意: 求将[1,n]划分成两个集合, 且两集合的和的差尽量小. 和/2为偶数最小差一定为0, 和/2为奇数一定为1. 显然可以通过某个前缀和删去一个数得到. #include <iostrea ...

  6. Codeforces 772C 构造 数学 + dp + exgcd

    首先我们能注意到两个数x, y (0 < x , y < m) 乘以倍数互相可达当且仅当gcd(x, m) == gcd(y, m) 然后我们可以发现我们让gcd(x, m)从1开始出发走 ...

  7. Jzzhu and Apples CodeForces - 449C (构造,数学)

    大意: 求从[1,n]范围选择尽量多的数对, 使得每对数的gcd>1 考虑所有除2以外且不超过n/2的素数p, 若p倍数可以选择的有偶数个, 直接全部划分即可 有奇数个的话, 余下一个2*p不划 ...

  8. Gluttony CodeForces - 892D (构造,思维)

    题面: You are given an array a with n distinct integers. Construct an array b by permuting a such that ...

  9. Necklace CodeForces - 613C (构造)

    链接 大意: 给定n种珠子, 第i种有$a_i$个, 求将珠子穿成项链, 使得能使切开后的项链回文的切口尽量多 若有一种以上珠子为奇数, 显然不能为回文, 否则最大值一定是$gcd(a_1,a_2,. ...

随机推荐

  1. ltp-ddt smp_basic

    SMP_S_FUNC_DUAL_CORE source functions.sh; cmd="stress-ng --matrix 4 -t 10s --perf --matrix-size ...

  2. MySQL优化系列之一

    MySQL数据库常见的两个瓶颈是CPU和I/O. CPU在饱和的情况下一般发生在数据装入内存或者从磁盘上读取数据的时候,当装入的数据远大于 内存容量的时候,这时可能会发生I/O瓶颈, 如果是分布式应用 ...

  3. call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

    <?php function foobar($arg, $arg2) { echo __FUNCTION__, " got $arg and $arg2\n"; } clas ...

  4. django model的get和filter方法的区别

    django的get方法: 1django的get方法是从数据库的取得一个匹配的结果,返回一个对象,如果记录不存在的话,它会报错. 2如果你用django的get去取得关联表的数据的话,而关键表的数据 ...

  5. selenium与appium怎样联系

    appium是手机app端的自动化,它继承了webdriver(也就是selenium 2)          不过appium仍然需要通过selenium最后做测试工具,但是appium起到了一个连 ...

  6. Spring学习总结(2)- AOP

    一,什么是AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中 ...

  7. 【HDOJ6606】Distribution of books(二分,BIT)

    题意:给定一个长为n的数组,要求挑它前缀的一段,将其分成k段,使得每段和的最大值最小 1<=k<=n<=2e5,abs(a[i])<=1e9 思路: 刚开始写了线段树TLE 改 ...

  8. 牛客假日团队赛9 A 乘积最大 (简单DP)

    题目:https://ac.nowcoder.com/acm/contest/1071/A 题意:给你一个串,然后给你m个乘号,用m个乘号分割开这个串,然后求分割可以求出的最大值 思路:首先范围很小 ...

  9. CocoaPods中pod search报错的解决办法

    pod search报错的原因: 每次使用pod search命令的使用会在该目录下生成一个search_index.json文件,会逐渐的增大,文件达到一定大小后,ruby解析里面的json字符就会 ...

  10. [CSP-S模拟测试76]题解

    咕咕咕 A.序列 无解情况:$n>a*b$或$n<a+b-1$ 把序列分成B段,每段内部上升,各段分界处构成下降子序列. 实现并不是太简单,要动态地考虑一下边界什么的. #include& ...