Codeforces 1188A 构造
題意:給你一顆樹(shù),樹(shù)的邊權(quán)都是偶數(shù),并且邊權(quán)各不相同。你可以選擇樹(shù)的兩個(gè)葉子結(jié)點(diǎn),并且把兩個(gè)葉子結(jié)點(diǎn)之間的路徑加上一個(gè)值(可以為負(fù)數(shù)),問(wèn)是否可以通過(guò)這種操作構(gòu)造出這顆樹(shù)?如果可以,輸出構(gòu)造方案。初始樹(shù)的邊權(quán)都是0。
思路:A1很簡(jiǎn)單,只要判斷是否有度數(shù)為2的點(diǎn)就可以了。對(duì)于A2, 由于邊權(quán)各不相同,所以A1的結(jié)論同樣適用。現(xiàn)在我們來(lái)構(gòu)造一組答案。官方題解的構(gòu)造方式是這樣的:我們假設(shè)要讓一個(gè)節(jié)點(diǎn)u到葉子結(jié)點(diǎn)v的路徑都加上一個(gè)值x,并且知道葉子結(jié)點(diǎn)l1, l2都可以到達(dá)u,我們執(zhí)行以下操作:v到l1的路徑加上x(chóng) / 2, v到l2的路徑加上x(chóng) / 2, l1 到 l2的路徑加上-x / 2,這樣除了u到v的路徑,其它路徑的值沒(méi)有變(太菜了,想不到。。。)。那么,我們從樹(shù)根開(kāi)始,從上到下逐個(gè)構(gòu)造邊權(quán)即可。
由于n只有1000,所以實(shí)現(xiàn)方式有兩種。
第一種很暴力,賦值操作直接暴力加,復(fù)雜度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);} }第二種用了類似樹(shù)剖中重兒子的思想,我們給一顆子樹(shù)中決定一個(gè)優(yōu)先級(jí)最高的葉子結(jié)點(diǎn),這樣加的操作是這個(gè)葉子結(jié)點(diǎn)到它的祖先的路徑上進(jìn)行的,其它的路徑?jīng)]有影響,這樣累加影響的時(shí)候,如果這個(gè)葉子結(jié)點(diǎn),把前面的影響累加上,否則不加。復(fù)雜度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);} }兩份代碼中為了實(shí)現(xiàn)方便,都找了一個(gè)度為1的點(diǎn)為根。
?
轉(zhuǎn)載于:https://www.cnblogs.com/pkgunboat/p/11145961.html
總結(jié)
以上是生活随笔為你收集整理的Codeforces 1188A 构造的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python中通过元类(TYPE)简单实
- 下一篇: Unity 动画属性