[BZOJ1977][Beijing2010组队][LCA][Kruskal]次小生成树

[Problem Description]
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)   这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
[Algorithm]
LCA ST 最小生成树Kruskal
[Analysis]
首先求出最小生成树。可以想到严格次小生成树就是将一条边与最小生成树中的一条边进行替换,要求必须要替换代价且替换代价最小。枚举每条没有在最小生成树中的边,求出这条边连接的两个点在最小生成树中路径上的最大的边(这样替换代价最小)。由于这条边和两点间最小生成树上的路径构成了一个环,所以从环上任意去掉一个边,点都是联通的。至于求两点树上路径最大边,用lca的st算法。还要注意的是,由于是严格次小生成树,所以还要维护两点间树上路径上次大的边,当两点间树上路径最大边与枚举的边相等的时候,将次大的边与枚举的边替换。
[Pay Attention]
LCA算法注意别忘了将x和y更新!!我因为这个问题调了半天……以后要注意使用静态查错了……
[Code]
/**************************************************************
    Problem: 1977
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:2920 ms
    Memory:35596 kb
****************************************************************/
 
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <iostream>
using namespace std;
 
const int MAXN = 100100;
const int MAXM = 300100;
const int MAXBIT = 22;
const long long INF = 1e15;
 
struct edge
{
    int x, y, z;
    bool operator < (const edge b) const
    {
        return z < b.z;
    }
} e[MAXM];
 
int point[MAXN] = {0}, next[MAXM] = {0}, v[MAXM], w[MAXM];
int tot = 0;
 
bool check[MAXM] = {0};
int deep[MAXN] = {0}, f[MAXN][MAXBIT] = {0}, maxedge[MAXN][MAXBIT] = {0}, lessedge[MAXN][MAXBIT] = {0};
int n, m;
long long ans = INF;
long long minCost = 0;
int theMaxBit;
 
int father[MAXN];
 
inline void UnionInit()
{
    for (int i = 1; i <= n; i++)
        father[i] = i;
}
 
inline void addedge(int x, int y, int z)
{
    tot++;
    next[tot] = point[x];
    point[x] = tot;
    v[tot] = y;
    w[tot] = z;
}
 
int getfather(int x)
{
    if (father[x] == x) return x;
    else return father[x] = getfather(father[x]);
}
 
inline void update(int &theMax, int &theLess, int x1, int y1, int x2, int y2)
{
    if (x1 > x2)
    {
        theMax = x1;
        theLess = max(y1, x2);
    }
    else if (x1 < x2)
    {
        theMax = x2;
        theLess = max(x1, y2);
    }
    else
    {
        theMax = x1;
        theLess = max(y1, y2);
    }
}
 
inline void MakeLca()
{
    queue<int> q;
    q.push(1);
    deep[1] = 1;
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        for (int temp = point[now]; temp; temp = next[temp])
            if (v[temp] != f[now][0])
            {
                q.push(v[temp]);
                f[v[temp]][0] = now;
                maxedge[v[temp]][0] = w[temp];
                lessedge[v[temp]][0] = 0;
                deep[v[temp]] = deep[now] + 1;
            }
    }
    //theMaxBit = MAXBIT;
    theMaxBit = (int)(log(n) / log(2) + 1);
    for (int Bit = 1; Bit <= theMaxBit; Bit++)
        for (int i = 1; i <= n; i++)
        {
            f[i][Bit] = f[f[i][Bit - 1]][Bit - 1];
            update(maxedge[i][Bit], lessedge[i][Bit], 
                   maxedge[i][Bit - 1], lessedge[i][Bit - 1],
                   maxedge[f[i][Bit - 1]][Bit - 1], lessedge[f[i][Bit - 1]][Bit - 1]);
        }
}
 
inline void Up(int &x, int tar, int &theMax, int &theLess)
{
    for (int i = theMaxBit; i >= 0; i--)
        if (deep[f[x][i]] >= tar)
        {
            update(theMax, theLess, theMax, theLess, maxedge[x][i], lessedge[x][i]);
            x = f[x][i];
        }
}
 
void Query(int x, int y, int &theMax, int &theLess)
{
    theMax = -1;
    theLess = -1;
    if (deep[x] < deep[y])
    {
        int temp = x;
        x = y;
        y = temp;
    }
    Up(x, deep[y], theMax, theLess);
    if (x == y) return;
    for (int i = theMaxBit; i >= 0; i--)
        if (f[x][i] != f[y][i])
        {
            update(theMax, theLess, theMax, theLess,
                   maxedge[x][i], lessedge[x][i]);
            update(theMax, theLess, theMax, theLess,
                   maxedge[y][i], lessedge[y][i]);
            x = f[x][i]; y = f[y][i];
        }
    update(theMax, theLess, theMax, theLess,
           maxedge[x][0], lessedge[x][0]);
    update(theMax, theLess, theMax, theLess,
           maxedge[y][0], lessedge[y][0]);
}
 
int main()
{
    //freopen("input.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    UnionInit();
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
    sort(e + 1, e + 1 + m);
    for (int i = 1; i <= m; i++)
    {
        int fx = getfather(e[i].x);
        int fy = getfather(e[i].y);
        if (fx != fy)
        {
            check[i] = true;
            father[fx] = fy;
            addedge(e[i].x, e[i].y, e[i].z);
            addedge(e[i].y, e[i].x, e[i].z);
            minCost += e[i].z;
        }
    }
    MakeLca();
    for (int i = 1; i <= m; i++)
        if (!check[i])
        {
            int theMax, theLess;
            Query(e[i].x, e[i].y, theMax, theLess);
            if (theMax != e[i].z)
            {
                long long temp = minCost - theMax;
                temp += e[i].z;
                ans = temp < ans ? temp : ans;
            }
            else if (theLess > 0)
            {
                long long temp = minCost - theLess;
                temp += e[i].z;
                ans = temp < ans ? temp : ans;
            }
        }
    printf("%lld\n", ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值