[BJOI]2010 严格次小生成树

题意:给出一个无向图,求其严格最小生成树(废话) n <= 100000
分析:
一般的做法:在MST上断一条边,再次求MST,取最小的值,最快的速度应该是堆优化Prim算法 O(n^2 log2n),但是在本题中显然会TLE
题目很裸,只能直接看这个模型。
次小的生成树,一定是MST上加了某一条边,形成了一个环,接着再删去某一条边得来的。加的边肯定没有特殊的决策,只好枚举,但是删去的边有玄机。由MST的性质,加上的这一条边肯定不小于环上的边,如果它比环上最大的边要大,那么答案一定就是 MST + 这条边 - 环上的边 ,可是如果它们相等呢?显然,求环上次大的边。
可是怎么快速求出”一条边连接两个顶点在MST上形成的环的最大值和次大值?”
嗯..这是个很常见的模型,倍增法可以在O(log2n)的时间内解决
Ps:第一次做,求出次大的还真不好处理,这里值得注意一下

/*I am firing!*/
#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
typedef int _int;
#define int long long
typedef long long LL;
const int Lim = 100005;

int n , m , cnt_edge , maxx1 , maxx2;
LL Ans , Ans_second = LLONG_MAX / 2;
int dep[Lim] , Max[Lim][20] , sec[Lim][20] , fa[Lim][20];
int head[Lim] , prt[Lim];
bool vst[Lim * 3];
struct St {int next , to , val;} e[Lim * 3];
struct E {
    int a , b , c;
    friend bool operator < (const E & A , const E & B)
        {return A.c < B.c;}
} edge[Lim * 3];
void Addstar(int x , int y , int z)
    {e[++cnt_edge] = (St) {head[x] , y , z}; head[x] = cnt_edge;}
int Getfather(int x) {return x == prt[x] ? x : prt[x] = Getfather(prt[x]);}
void Dfs(int x)
{
    for(int i=head[x] ; i ; i=e[i].next)
    {
        int y = e[i].to;
        if(y == fa[x][0]) continue;
        dep[y] = dep[x] + 1;
        Max[y][0] = e[i].val;
        fa[y][0] = x;
        Dfs(y);
    }
}
/*另一个地方的最大可能不如这里的次大*/

void ST()
{
    for(int j=1;j<=18;j++)
        for(int i=1;i<=n;i++)
            if(fa[i][j-1])
            {
                fa[i][j] = fa[fa[i][j-1]][j-1];
                int M1 = Max[i][j-1] , M2 = Max[fa[i][j-1]][j-1];
                int S1 = sec[i][j-1] , S2 = sec[fa[i][j-1]][j-1];
                if(M1 == M2) sec[i][j] = min(S1 , S2);
                    else if(M1 > M2) sec[i][j] = max(S1 , M2);
                        else sec[i][j] = max(S2 , M1);
                Max[i][j] = max(M1 , M2);   
            }
}

inline void update(long long a, long long b)
{
    if (a > maxx1)
    {
        maxx2 = maxx1;
        maxx1 = a;
        if (b > maxx2)
            maxx2 = b;
    }
    else if (a > maxx2 && a != maxx1)
            maxx2 = a;
}

void LCA(int a , int b)
{
    if(dep[a] < dep[b]) swap(a , b);
    for(int i=18;i>=0;i--)
        if(dep[a] - (1<<i) >= dep[b])
        {
            update(Max[a][i] , sec[a][i]);
            a = fa[a][i];
        }

    if(a == b) return;
    for(int i=18;i>=0;i--)
        if(fa[a][i] != fa[b][i])
        {
            update(Max[a][i] , sec[a][i]);
            update(Max[b][i] , sec[b][i]);
            a = fa[a][i] , b = fa[b][i];
        }

    update(Max[a][0] , sec[a][0]);
    update(Max[b][0] , sec[b][0]);
}



void Kruscal(int k = 0)
{
    for(int i=1;i<=n;i++) prt[i] = i;
    sort(edge+1,edge+1+m);
    for(int i=1;i<=m;i++)
    {
        int f1 = Getfather(edge[i].a) , f2 = Getfather(edge[i].b);
        if(f1 != f2)
        {
            prt[f2] = f1;
            vst[i] = true;
            Ans += edge[i].c;
            Addstar(edge[i].a , edge[i].b , edge[i].c);
            Addstar(edge[i].b , edge[i].a , edge[i].c);
            if(++k == n-1) break;
        }
    }
}

_int main()
{
    scanf("%lld %lld",&n,&m);
    for(int i=1,x,y,z;i<=m;i++)
    {
        scanf("%lld %lld %lld",&x,&y,&z);
        edge[i] = (E) {x , y , z};
    }
    Kruscal();
    Dfs(1);

    ST();
    for(int i=1;i<=m;i++)
        if(!vst[i])
        {
            maxx1 = maxx2 = -1;
            LCA(edge[i].a , edge[i].b);
            int t1 = Ans + edge[i].c - maxx1;
            if(t1 > Ans) Ans_second = min(Ans_second , t1);
            int t2 = Ans + edge[i].c - maxx2;
            if(t2 > Ans) Ans_second = min(Ans_second , t2);
        }
    cout<<Ans_second;       

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值