zoukankan      html  css  js  c++  java
  • bzoj 3246 [ioi2013]Dreaming 贪心

    有n个点,由m条边连接,第i条边的边权是wi。这些点和边构成了一个森林。你必须要新建若干条条权值为W天的边,使得原图恰好变成一棵树,并且让任意两个点间最长距离最短。求该通行时间
    思路:
    首先找出每棵树的直径和中心及其对应半径。
    加边的过程一定是在这些中心之间加边。
    考虑这些中心的连接方式。
    选择一个中心,让其它中心都直接连上这个点肯定是最优的,中心点为最大半径所在的那个点。
    则答案要么是原树中的直径,要么是加边以后的新的直径。
    新的直径分两种情况,一种是最大半径和次大半径直径直接用一条新边相连,另一种是次大边和第三大边用两条新边相连。

    更简单的做法是:将中心点与其它点的半径点连边,在原图上哟,然后跑一次新树的直径。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    
    const int N = 500010;
    int n, m, cnt;
    LL w, r, ans;
    int head[N], son[N];
    LL f[N], g[N];
    bool vis[N];
    struct Edge
    {
        int to, nex; LL v;
        Edge() {}
        Edge(int x, int y, LL z) { to = x, nex = y, v = z; }
    }e[N<<1];
    
    inline void addEdge(int x, int y, LL z)
    { e[++ cnt] = Edge(y, head[x], z); 
    head[x] = cnt; 
    }
    inline void dfs(int u, int lst) //找出以u为根时,每个点的最长链及次长链 
    {
        vis[u] = 1; 
        f[u] = g[u] = 0;
        for(int i = head[u]; i; i = e[i].nex) 
        {
            int v = e[i].to; 
            if(v == lst) continue;
            dfs(v, u);
            if(f[v]+e[i].v > f[u]) 
            son[u] = v, f[u] = f[v]+e[i].v;
        }
        for(int i = head[u]; i; i = e[i].nex)
            if(e[i].to != lst && e[i].to != son[u]) 
                if(f[e[i].to]+e[i].v > g[u]) 
                     g[u] = f[e[i].to]+e[i].v;
    }
    inline void dfs2(int u, int lst, LL d)
    //u代表当前点,lst代表其父亲点,d代表距离
    //采用换根法求出每个点到其它点的最长链及次长链 
    {
        if(d > f[u]) 
             g[u] = f[u], f[u] = d, son[u] = lst;
        else 
            if(d > g[u]) 
              g[u] = d;
        r = min(r, f[u]); //r代表树的半径,其为所有点的最长链的最小值 
        ans = max(ans, f[u]);//必然会找到某个叶子点,其最长链即为这个树的直径 
        for(int i = head[u]; i; i = e[i].nex) 
        if(e[i].to != lst) 
        {
            if(e[i].to != son[u]) 
                   dfs2(e[i].to, u, f[u]+e[i].v);
            else 
                dfs2(e[i].to, u, g[u]+e[i].v);
        }
    }
    int main()
    {
        scanf("%d%d%lld", &n, &m, &w);
        for(int i = 1; i <= m; i ++) 
        {
            int x, y; LL z; 
            scanf("%d%d%lld", &x, &y, &z);
            x ++, y ++; 
            addEdge(x, y, z); addEdge(y, x, z);
        }
        LL mx = -1e18,mx2 = -1e18, mx3 = -1e18;//这边一定要赋最小,-1e9也没用。
        for(int i = 1; i <= n; i ++) 
        if(!vis[i]) 
        {
            r = 1e18; 
            dfs(i, 0); 
            dfs2(i, 0, 0);
            //求出每个树的半径后,求所有半径的最长,次长,及第三长 
            if(r > mx) mx3 = mx2, mx2 = mx, mx = r;
            else if(r > mx2) mx3 = mx2, mx2 = r;
            else if(r > mx3) mx3 = r;
        }
        ans = max(ans, mx+w+mx2);
        ans = max(ans, mx2+mx3+w+w);
        printf("%lld\n", ans);
        return 0;
    }
  • 相关阅读:
    JAVA学习(运算符2)
    JAVA学习(运算符)
    Android 手机配office365邮箱
    如何使用思维导图更高效的设计测试用例?(转)
    How to fix broken packages?(转)
    思维导图–云笔记测试的利器(转)
    软件测试中过度设计的那些事儿(转)
    基于矩阵模式的 Web 软件测试手段(转)
    Github原理
    GitHub 教程 in Ubuntu
  • 原文地址:https://www.cnblogs.com/cutemush/p/11695055.html
Copyright © 2011-2022 走看看