Codeforces Round #572 (Div. 2) D2. Add on a Tree: Revolution (搜索好题)

题目链接:http://codeforces.com/contest/1189/problem/D2

题意:现在有一颗 n n n个节点的树,树的边上有一个权值(保证为偶数),你需要将一个空树(形状和给出的树相同边权为 0 0 0)变成给出的树,在空树中,你可以多次任意选树的两个叶子节点,然后选择一个整数,将两个叶子节点路径上的所有边权值加上这个整数,问是否可以将这个空树变成给出的树,如果不能输出 N O NO NO,否则输出 Y E S YES YES并且输出你每次选择的叶子节点和权值。

解题心得:

  • 首先说不能的情况,这就是D1的结论,只要出现D1判断的情况直接输出 N O NO NO
  • 然后想一想如果你选择一条边要将这个边权值从 0 0 0变成给出树边的权值有几种情况呢:
    • 第一种:这个边两边的节点都是叶子节点,这样直接加上目标权值就行了。
    • 第二种:这个边的有一个节点是叶子节点记为 y 1 y1 y1,另一个节点是在树中的一个节点记为 x x x,这个时候必然可以从 x x x节点在 y 1 y1 y1节点的反方向(深度搜索)找到两个的叶子节点,可以记为 y 2 y2 y2 y 3 y3 y3,这个时候要将 y 1 y1 y1 x x x的边加上一个值 v a l val val,可以将 y 1 y1 y1 y 2 y2 y2路径上加上 v a l / 2 val/2 val/2 y 1 y1 y1 y 3 y3 y3路径上加上 v a l / 2 val/2 val/2 y 2 y2 y2 y 3 y3 y3路径上减去 v a l / 2 val/2 val/2,因为 v a l val val一定是偶数,所以 v a l / 2 val/2 val/2一定是整数。这样算起来是不是就是在 y 1 y1 y1 x x x之间的边上加上了 v a l val val并且消除了其他影响,类似于容斥原理。
    • 第三种:一条边的两个端点都不是叶子节点,记两个节点为 x 1 x1 x1, x 2 x2 x2 x 1 x1 x1可以在 x 2 x2 x2的反方向找到两个叶子节点记为 y 1 y1 y1, y 2 y2 y2 x 2 x2 x2可以在 x 1 x1 x1的反方向找到两个叶子节点记为 y 3 y3 y3, y 4 y4 y4,这时需要在 x 1 x1 x1 x 2 x2 x2的边上加的值为val,可以这样操作,在 y 1 y1 y1 y 3 y3 y3两个叶子节点路径上加上 v a l / 2 val/2 val/2,在 y 2 和 y 4 y2和y4 y2y4两个叶子节点路径上加上 v a l / 2 val/2 val/2,在 y 1 y1 y1 y 2 y2 y2两个叶子节点路径上减去 v a l / 2 val/2 val/2,在 y 3 y3 y3 y 4 y4 y4两个叶子节点路径上减去 v a l / 2 val/2 val/2
  • 因为数据量比较小,写得暴力一点也没有关系。


#include 
using namespace std;
typedef complex<double> cp;
typedef long long ll;
typedef pair<int,int> P;
const ll maxn = 1010;
const double pi = acos(-1);

map <pair<int, int>, int> maps;//用于记录这条边是否被处理过

struct Edge {
     //记录每次操作选择的叶子节点和值
    int u, v, val;
};

int n, degree[maxn];//记录每个节点的度
vector <pair<int, int>> ve[maxn];//记录树的边
vector <Edge> ans;//记录答案


void init() {
     
    scanf("%d", &n);
    for(int i=1;i<n;i++) {
     
        int a, b, c;
        scanf("%d%d%d",&a, &b, &c);
        ve[a].push_back(make_pair(b, c));//建立双向边
        ve[b].push_back(make_pair(a, c));
        degree[a]++;
        degree[b]++;
    }
}

bool Impossible() {
     //判断不可能的情况
    for(int i=1;i<=n;i++) {
     
        if(degree[i] == 2) {
     
            return true;
        }
    }
    return false;
}

vector <int> node[2];//记录边连接的两个节点对应方向的两个叶子节点,如果自身是叶子节点就将自身压入

void find_node(int father, int v, int index) {
     //找非叶子节点的另一个方向的两个叶子节点
    if(degree[v] == 1) {
     
        node[index].push_back(v);
        return ;
    }

    for(int i=0;i<ve[v].size();i++) {
     
        int to = ve[v][i].first;
        if(to == father) continue;
        find_node(v, to, index);
        return ;
    }
}

void get_ans(int u, int v, int val) {
     
    if(degree[u] != 1) {
     //判断u是否是叶子节点,不是就去找另外两个叶子节点
        for(int i=0;i<ve[u].size();i++) {
     
            int to = ve[u][i].first;
            if(to == v) continue;
            else {
     
                find_node(u, to, 0);
                if(node[0].size() == 2) break;
            }
        }
    } else {
     
        node[0].push_back(u);
    }

    if(degree[v] != 1) {
     //判断v是否是叶子节点,不是就去找另外两个叶子节点
        for(int i=0;i<ve[v].size();i++) {
     
            int to = ve[v][i].first;
            if(to == u) continue;
            else {
     
                find_node(v, to, 1);
                if(node[1].size() == 2) break;
            }
        }
    } else {
     
        node[1].push_back(v);
    }

    if(node[0].size() == 2) {
     //各种条件下有不同的答案
        ans.push_back({
     node[0][0], node[0][1], -val/2});
        if(node[1].size() == 2) {
     
            ans.push_back({
     node[1][0], node[1][1], -val/2});
            ans.push_back({
     node[0][0], node[1][0], val/2});
            ans.push_back({
     node[0][1], node[1][1], val/2});
        } else {
     
            ans.push_back({
     node[0][1], node[1][0], val/2});
            ans.push_back({
     node[0][0], node[1][0], val/2});
        }
    } else {
     
        if(node[1].size() == 2) {
     
            ans.push_back({
     node[1][0], node[1][1], -val/2});
            ans.push_back({
     node[0][0], node[1][0], val/2});
            ans.push_back({
     node[0][0], node[1][1], val/2});
        } else {
     
            ans.push_back({
     node[0][0], node[1][0], val});
        }
    }
    node[0].clear();
    node[1].clear();
}

void solve() {
     
    for(int i=1;i<=n;i++) {
     
        for(int j=0;j<ve[i].size();j++) {
     
            int to = ve[i][j].first;
            int u = i;
            if(u > to) swap(u, to);
            if(maps.count(make_pair(u, to)) != 0) continue;//处理过的边就不再处理
            maps[make_pair(u, to)] = 1;
            get_ans(i, to, ve[u][j].second);
        }
    }
}

int main() {
     
    //    freopen("1.in.txt", "r", stdin);
    init();

    if(Impossible()) {
     
        puts("NO");
        return 0;
    } else {
     
        puts("YES");
    }

    solve();

    printf("%d\n", ans.size());
    for(int i=0;i<ans.size();i++) {
     
        Edge now = ans[i];
        printf("%d %d %d\n",now.u, now.v, now.val);
    }
    return 0;
}

你可能感兴趣的:(搜索-DFS,搜索,思维)