HDU 4126 Genghis Khan the Conqueror

传送门

这个题借鉴了别人思想自己写的,题目大意是,给定图,保证每两点之间最多有一条直接相连的边,先给出原始图,然后Q次更新,每次增加某条边的权值,每次更新独立,求出本次的mst值,最后求平均。
所以问题就是根据每次更新以及原始mst求最新mst。两种情况,
- 更新边不是原始mst上的。所以还是原始mst的值。
- 更新边是原始mst上的。因为更新一定是增加,所以这时这条边可能有一条原始mst以外的边来替代。
那么无论原始mst上的边权值怎样增加,只要事先求出原始mst上每条边的最佳替换边(其实就是一个非mst边的权值)就可以了,然后再和更新值比大小。
那么就把非mst上的边从小到大排序,每条边在原始mst上一定形成一个环,那么除该边之外环上所有边的替换边就是他。这个过程用dfs实现(从非mst边出发,只走mst边,走一个环)。
因为从小到大排序,所以每条mst上的边的替换值要么为初始值(-1,这时意味替换值不存在),要么只能被赋予一次。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
using namespace std;

const int INF = 1e9;
const int MAXN = 1e3 * 3 + 2;
int N, M, Q;

struct Edge
{
    int n1, n2, w, flag;
    bool operator<(const Edge& e) const
    {
        return w < e.w;
    }
};
vector<Edge> ve;
vector<pair<int, int>> v[MAXN];
int mst;
int pre[MAXN];
int conn;
int rep[MAXN][MAXN];
int counter;
long long total;

void init()
{
    ve.clear(); //
    mst = 0;
    memset(pre, -1, sizeof pre);
    conn = 0;
    memset(rep, -1, sizeof rep);
    for (int i = 0; i < N; i++) v[i].clear();
    counter = 0;
    total = 0;
}

int f(int x)
{
    int f0 = x, f1 = x, t;
    for (; pre[f0] >= 0;) f0 = pre[f0];
    for (; pre[f1] >= 0;)
    {
        t = f1;
        f1 = pre[f1];
        pre[t] = f0;
    }
    return f0;
}

bool u(int n1, int n2)
{
    int f1 = f(n1);
    int f2 = f(n2);
    if (f1 != f2)
    {
        conn++;
        if (pre[f1] <= pre[f2])
        {
            pre[f1] += pre[f2];
            pre[f2] = f1;
        }
        else
        {
            pre[f2] += pre[f1];
            pre[f1] = f2;
        }
        return true;
    }
    return false;
}

bool dfs(int n, int f, int w, int prev)
{
    if (n == f) return true;
    for (int i = 0; i < v[n].size(); i++)
    {
        if (v[n][i].first != prev)                    //
        {
            if (dfs(v[n][i].first, f, w, n))
            {
                if (rep[n][v[n][i].first] == -1)
                {
                    rep[n][v[n][i].first] = rep[v[n][i].first][n] = w;
                    counter++;
                }
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int a, b, c;
    for (; ~scanf("%d%d", &N, &M) && N + M;)
    {
        init();
        for (int i = 0; i < M; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            ve.push_back({ a,b,c,0 });
        }
        sort(ve.begin(), ve.end());
        for (int i = 0; i < ve.size(); i++)
        {
            if (u(ve[i].n1, ve[i].n2))
            {
                mst += ve[i].w;
                ve[i].flag = 1;
                v[ve[i].n1].push_back(make_pair(ve[i].n2, ve[i].w));
                v[ve[i].n2].push_back(make_pair(ve[i].n1, ve[i].w));    // 邻接表只加mst上的边
            }
            if (conn == N - 1) break;
        }
        for (int i = 0; i < ve.size(); i++)
        {
            if (!ve[i].flag)
            {
                dfs(ve[i].n1, ve[i].n2, ve[i].w, -1);
                if (counter == N - 1) break;                // 可以提前退出,然而这里可能到最后counter还没到N-1 ! 
            }                                               // 也就是说 可能不能 为所有mst上的边匹配最佳替换边!
        }
        scanf("%d", &Q);
        bool flag;
        int old;
        for (int i = 0; i < Q; i++)
        {
            flag = 0;
            scanf("%d%d%d", &a, &b, &c);
            for (int j = 0; j < v[a].size(); j++)
            {
                if (v[a][j].first == b)
                {
                    flag = 1;
                    old = v[a][j].second;
                    break;
                }
            }
            if (flag)                                          // 所谓最佳替换边也不一定要用,还要和原mst上的边的最新值比较一下看谁更小
            {
                if (rep[a][b] == -1) rep[a][b] = INF;          // 重点来了!!      等于-1说明该边没有替换边!!!
                total += mst - old + min(c, rep[a][b]);
            }
            else total += mst;
        }
        printf("%.4f\n", (double)total / (double)Q);
    }

    return 0;
}
posted @ 2017-04-18 21:41  CrossingOver  阅读(95)  评论(0编辑  收藏  举报