[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; }