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

    package java08; /* java.util.Arrays是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作 public static String toStri ...

  2. UVa1636 Headshot 【迁移自洛谷博客】

    说明:小蒟蒻hkk现在正在做一些概率的题目,由于这方面和数学还有点关系,所以需要一些数学的思维,也需要表述出来,如夏军所述"把自己给讲懂",所以写了些blog,主要为帮助自己理解. ...

  3. [Luogu2170]选学霸

    这一道题,由于他说,"如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议."而要求"既不让同学们抗议,又与原来的M尽可能接近".因此,我们要对实力相 ...

  4. 运行rabbitmq

    docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq c4663bdca2cd

  5. boost IOStreams

    Boost.IOStreams provides numerous implementations of the two concepts. Devices which describes data ...

  6. mui is not defined

    vue项目中引用mui.js,我是在main.js中这样引入的,    结果报错  查找资料,最后在mui.js的最后添加了这样一句  这是因为mui并不能像jquery那样作为全局对象存在,加上wi ...

  7. Ext js-02 -官方API文档使用

    官方API文档地址: http://docs.sencha.com/extjs/6.5.3/classic/Ext.html 打开网页如下: 1.选择所使用的Ext js版本,后面offline do ...

  8. iOS 常用随机数

    比如:获取一个随机整数范围在:[0,100)包括0,不包括100 ; 参考:https://www.jianshu.com/p/106475cbd3da

  9. SDK与API的理解

    1.SDK SDK (Software Development Kit):软件开发工具包,一般都是软件工程师为特定的软件包.软件框架.硬件平台.操作系统等建立应用软件时的开发工具的集合. SDK (S ...

  10. click和blur冲突的问题

    昨天在前端群里讨论到一个问题,大家平时做表单验证的时候一般都有个input框和删除按钮,然后习惯性在失去焦点的时候去验证输入的内容是否正确,做验证,发请求等等.这个时候,那个点击删除按钮往往也就触发了 ...