zoukankan      html  css  js  c++  java
  • 【19.27%】【codeforces 618D】Hamiltonian Spanning Tree

    time limit per test2 seconds
    memory limit per test256 megabytes
    inputstandard input
    outputstandard output
    A group of n cities is connected by a network of roads. There is an undirected road between every pair of cities, so there are roads in total. It takes exactly y seconds to traverse any single road.

    A spanning tree is a set of roads containing exactly n - 1 roads such that it’s possible to travel between any two cities using only these roads.

    Some spanning tree of the initial network was chosen. For every road in this tree the time one needs to traverse this road was changed from y to x seconds. Note that it’s not guaranteed that x is smaller than y.

    You would like to travel through all the cities using the shortest path possible. Given n, x, y and a description of the spanning tree that was chosen, find the cost of the shortest path that starts in any city, ends in any city and visits all cities exactly once.

    Input
    The first line of the input contains three integers n, x and y (2 ≤ n ≤ 200 000, 1 ≤ x, y ≤ 109).

    Each of the next n - 1 lines contains a description of a road in the spanning tree. The i-th of these lines contains two integers ui and vi (1 ≤ ui, vi ≤ n) — indices of the cities connected by the i-th road. It is guaranteed that these roads form a spanning tree.

    Output
    Print a single integer — the minimum number of seconds one needs to spend in order to visit all the cities exactly once.

    Examples
    input
    5 2 3
    1 2
    1 3
    3 4
    5 3
    output
    9
    input
    5 3 2
    1 2
    1 3
    3 4
    5 3
    output
    8
    Note
    In the first sample, roads of the spanning tree have cost 2, while other roads have cost 3. One example of an optimal path is .

    In the second sample, we have the same spanning tree, but roads in the spanning tree cost 3, while other roads cost 2. One example of an optimal path is .

    【题解】

    给你一个完全图n*(n-1)/2的图;
    这个图的所有边边权都为y;
    然后再对这个图的某个生成树上的边进行修改;这个生成树上的边边权从y改成x;
    (这个生成树就是给你的n-1条边组成的树);
    然后让你求遍历所有的边的最小边权和(每个点只能走一次);
    如果x小于y;
    则我们最后的路径应该在那个所给的生成树上的边尽可能地多;
    怎样保证呢?
    从任意一个点进行dfs(假如从1点开始);
    每个点可以连接点边数肯定是2(大于3就不能保证每个点只走一次);
    这里写图片描述
    上图是样例输入的情形。
    最后dfs能形成两条路径
    1->2
    和5->3->4
    然后从这两个子图里面比如引一条2指向5的y边权边过去;就能遍历整张图了;
    这个dfs的依据就是每个点最多只能联通两条边;
    在dfs的时候记录这个点还能连几条边;
    然后判断一下目标点能连几条边;如果都能连(都大于0)则连一条边;
    如果y<=x
    让非生成树边最多;
    其实只有没有一个点和其余n-1个点相连的情况都能保证遍历的时候全是非生成树边;因为你总可以和那个不能通过生成树边到达的点通过非生成树边到达;
    则特判一下就好;
    如果有一个点能和其他n-1个点相连答案就是x+(n-2)*y否则就都是(n-1)*y;

    #include <cstdio>
    #include <cmath>
    #include <set>
    #include <map>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <stack>
    #include <string>
    #define lson L,m,rt<<1
    #define rson m+1,R,rt<<1|1
    #define LL long long
    
    using namespace std;
    
    const int MAXN = 2e5+10;
    
    const int dx[5] = {0,1,-1,0,0};
    const int dy[5] = {0,0,0,-1,1};
    const double pi = acos(-1.0);
    
    int n;
    LL x,y,in=0;
    vector <int> a[MAXN];
    
    void input_LL(LL &r)
    {
        r = 0;
        char t = getchar();
        while (!isdigit(t)) t = getchar();
        LL sign = 1;
        if (t == '-')sign = -1;
        while (!isdigit(t)) t = getchar();
        while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
        r = r*sign;
    }
    
    void input_int(int &r)
    {
        r = 0;
        char t = getchar();
        while (!isdigit(t)) t = getchar();
        int sign = 1;
        if (t == '-')sign = -1;
        while (!isdigit(t)) t = getchar();
        while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
        r = r*sign;
    }
    
    bool dfs(int x,int fa)
    {
        int rest = 2;
        int len = a[x].size();
        for (int i =0 ;i <= len-1;i++)
        {
            int y = a[x][i];
            if (y==fa)
                continue;
            bool j = dfs(y,x);
            if (rest&&j)
            {
                rest--;
                in++;
            }
        }
        return rest>0;
    }
    
    int main()
    {
        //freopen("F:\rush.txt", "r", stdin);
        input_int(n);input_LL(x);input_LL(y);
        for (int i = 1;i <= n-1;i++)
        {
            int x0,y0;
            input_int(x0);input_int(y0);
            a[x0].push_back(y0);
            a[y0].push_back(x0);
        }
        if (x<y)
        {
            dfs(1,-1);
            LL ans = in*x + (n-1-in)*y;
            cout << ans << endl;
        }
        else
        {
            for (int i = 1;i <= n;i++)
            {
                int len = a[i].size();
                if (len == n-1)
                {
                    cout << x+(n-2)*y<<endl;
                    return 0;
                }
            }
            cout << (n-1)*y<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    Java精通并发-Lock锁方法原理详解
    Java精通并发-Lock锁机制深入详解
    深层次揭示runBlocking与coroutineScope之间的异同点
    轻量级协程与线程执行比对分析
    第二阶段冲刺个人任务——four
    第二阶段冲刺个人任务——three
    第二阶段冲刺个人任务——two
    第二阶段冲刺个人任务——one
    输入法评价
    2017.12.30第十五周学习进度
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632109.html
Copyright © 2011-2022 走看看