[BJWC2010] 严格次小生成树

传送门:>Here<

给出一张带权无向图,求其严格次小生成树。($n \leq 10^5$)

解题思路

图的生成树有一个性质:将一条非树边加入后必定形成环。Kruscal求最小生成树其实就是一个贪心地过程:剔除每个环上最大的那一条边。

那么反过来,求次小生成树就是在某个环上换回最大边,删去原来最大边以保证增量最小的过程。由于题目要求严格,会碰到最大边等于原先最大边的情况,此时就删去原先严格次大边。

于是现在就是一个数据结构维护的问题了。预处理出最小生成树,维护树链上最大值和次大值,可以树剖或者在倍增LCA的过程中维护。

如果够无聊,LCT也可以。LCT求最小生成树不用预先对边排序,由于可以动态连边断边,是完全可以根据最小生成树的性质直接维护的——每发现一条边形成了环,如果更优就直接LinkCut更新,保证了全局的最优性。再在Splay上维护边权的最小值就解决了。

$Code$

/*By QiXingzhi*/
#include <cstdio>
#include <vector>
#include <algorithm>
#define  N  (100010)
#define  M  (300010)
#define  INF   (21474836470000)
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
typedef long long ll;
using namespace std;
#define int ll
inline void read(int &x){
    x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); x *= w;
}
struct Edge{
    int x,y,z;
};
struct Graph{
    int to,cost;
};
Edge a[M];
bool t_edge[M];
vector <Graph> G[N];
vector <Graph> Gt[N];
int n,m,krus,krus_num,x,y,ans;
int par[N],f[N][30],gmax[N][30],gsec[N][30],fa[N],wei[N],depth[N];
inline void AddEdge(int u, int v, int w){
    Graph e;
    e.to = v;
    e.cost = w;
    G[u].push_back(e);
}
inline void AddEdge_2(int u, int v, int w){
    Graph e;
    e.to = v;
    e.cost = w;
    Gt[u].push_back(e);
}
inline void Swap(int& a, int &b){
    int t = a;
    a = b;
    b = t;
}
inline bool kruscal_sort_comp(const Edge& a, const Edge& b){
    return a.z < b.z;
}
int krus_find(int x){
    if(par[x] == x) return x;
    par[x] = krus_find(par[x]);
    return par[x];
}
void build_tree(int x, int ff){
    int sz = Gt[x].size();
    int to;
    for(int i = 0; i < sz; ++i){
        to = Gt[x][i].to;
        if(to != ff){
            fa[to] = x;
            wei[to] = Gt[x][i].cost;
            build_tree(to, x);
        }
    }
}
void lca_dfs(int x, int d){
    depth[x] = d;
    for(int k = 1; (1<<k) < d; ++k){
        f[x][k] = f[f[x][k-1]][k-1];
        gmax[x][k] = Max(gmax[x][k-1], gmax[f[x][k-1]][k-1]);
        if(gmax[x][k-1] == gmax[f[x][k-1]][k-1]){
            gsec[x][k] = Max(gsec[x][k-1], gsec[f[x][k-1]][k-1]);
        }
        else if(gmax[x][k-1] > gmax[f[x][k-1]][k-1]){
            gsec[x][k] = Max(gsec[x][k-1], gmax[f[x][k-1]][k-1]);
        }
        else if(gmax[x][k-1] < gmax[f[x][k-1]][k-1]){
            gsec[x][k] = Max(gmax[x][k-1], gsec[f[x][k-1]][k-1]);
        }
    }
    int sz = Gt[x].size();
    int to;
    for(int i = 0; i < sz; ++i){
        to = Gt[x][i].to;
        if(to != f[x][0]){
            lca_dfs(to, d+1);
        }
    }
}
inline void Update(int i){
    int u = a[i].x, v = a[i].y, w = a[i].z, res;
    int val1 = -INF, val2 = -INF;
    if(depth[u] > depth[v]) Swap(u,v);
    for(int k = 20; k >= 0; --k){
        if(depth[u] <= depth[v] - (1<<k)){
            if(gmax[v][k] < val1){
                val2 = Max(gmax[v][k], val2);
            }
            else if(gmax[v][k] == val1){
                val2 = Max(val2, gsec[v][k]);
            }
            else if(gmax[v][k] > val1){
                val2 = Max(gsec[v][k], val1);
            }
            val1 = Max(val1, gmax[v][k]);
            v = f[v][k];
        }
    }
    if(u != v){
        for(int k = 20; k >= 0; --k){
            if(f[u][k] != f[v][k]){
                if(gmax[u][k] < val1){
                    val2 = Max(val2, gmax[u][k]);
                }
                else if(gmax[u][k] == val1){
                    val2 = Max(val2, gsec[u][k]);
                }
                else{
                    val2 = Max(val1, gsec[u][k]);
                }
                if(gmax[v][k] < val1){
                    val2 = Max(val2, gmax[v][k]);
                }
                else if(gmax[v][k] == val1){
                    val2 = Max(val2, gsec[v][k]);
                }
                else{
                    val2 = Max(val1, gsec[v][k]);
                }
                val1 = Max(val1, gmax[u][k]);
                val1 = Max(val1, gmax[v][k]);
                u = f[u][k];
                v = f[v][k];
            }
        }
        val1 = Max(val1, Max(gmax[u][0], gmax[v][0]));
        if(val2 < gmax[u][0] && gmax[u][0] < val1) val2 = gmax[u][0];
        if(val2 < gmax[v][0] && gmax[v][0] < val1) val2 = gmax[v][0];
    }
    if(val1 < w){
        res = krus - val1 + w;
    }
    else if(val1 == w){
        res = krus - val2 + w;
    }
    ans = Min(ans, res);
}
#undef int
int main(){
    #define int ll
//    freopen(".in","r",stdin);
    read(n), read(m);
    for(int i = 1; i <= m; ++i){
        read(a[i].x), read(a[i].y), read(a[i].z);
        AddEdge(a[i].x,a[i].y,a[i].z);
        AddEdge(a[i].y,a[i].x,a[i].z);
    }
    sort(a+1, a+m+1, kruscal_sort_comp);
    for(int i = 1; i <= n; ++i) par[i] = i;
    for(int i = 1; i <= m; ++i){
        x = krus_find(a[i].x);
        y = krus_find(a[i].y);
        if(x != y){
            par[x] = y;
            ++krus_num;
            krus += a[i].z;
            t_edge[i] = 1;
            AddEdge_2(a[i].x,a[i].y,a[i].z);
            AddEdge_2(a[i].y,a[i].x,a[i].z);
        }
        if(krus_num >= n-1) break;
    }
    build_tree(1, 0);
    for(int i = 1; i <= n; ++i){
        f[i][0] = fa[i];
        gmax[i][0] = wei[i];
        gsec[i][0] = -INF;
    }
    lca_dfs(1,1);
    ans = INF;
    for(int i = 1; i <= m; ++i){
        if(!t_edge[i]){
            Update(i);
        }
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2018-06-29 13:22  DennyQi  阅读(530)  评论(2编辑  收藏  举报