【模板】嚴格次小生成樹[BJWC2010] --- kruskal重構樹 + LCA
阿新 • • 發佈:2018-11-10
傳送門:洛谷4180
題目大意
給出
個點,
條邊的無向圖,求嚴格次小生成樹.
即保證 次小生成樹的邊權和
最小生成樹的邊權和
分析
首先提供一條定理:次小生成樹一定由最小生成樹經過”邊交換”(加上一條邊再刪去一條邊)得到.
因此考慮先用 kruskal求出一顆
,然後列舉剩下的邊,依次加入再刪去
上一條邊(保證這條邊的權值嚴格小於加入邊權值,且最大)
問題的關鍵就在於如何求出要刪去的邊.
首先忽略掉"嚴格小於"這一條件的話,就只要求
上兩點間路徑上的最大值,對此可以考慮 用kruskal 重構樹來簡化一下(普通的LCA也行,只不過還要再掃一遍)
若
不唯一的話,還是求得
後再掃一遍吧(在kruskal重構樹上,兩葉子節點之間的點權,即為路徑上的邊權)
程式碼
洛谷上開 後 ,暫時
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define IL inline
using namespace std;
typedef long long ll;
IL int read()
{
int sum = 0;
bool k = 1;
char c = getchar();
for(;'0' > c || c > '9'; c = getchar())
if(c == '-') k = 0;
for(;'0' <= c && c <= '9'; c = getchar())
sum = sum * 10 + (c - '0');
return k ? sum : -sum;
}
int n, m;
struct Side
{
int u, v, w;
bool f;
bool operator < (const Side &b) const
{
return w < b.w;
}
}side[300005];
int fa[200005][18];
int dep[200005];
int weight[200005];
int lg[200005];
int pa[200005];
IL int find(int x) { return pa[x] = (pa[x] == x ? x : find(pa[x])); }
IL bool join(int x, int y, int z, int &T)
{
int x1 = find(x), y1 = find(y);
if(x1 == y1) return 0;
++T;
fa[x1][0] = fa[y1][0] = T + n;
weight[T + n] = z;
pa[x1] = pa[y1] = T + n;
return 1;
}
IL ll kru()
{
sort(side + 1, side + m + 1);
for(int i = 1; i <= n; ++i)
{
pa[i] = i; pa[i + n] = i + n;
}
ll sum = 0;
int T = 0;
for(int i = 1; i <= m && T < n; ++i)
if(join(side[i].u, side[i].v, side[i].w, T))
{
side[i].f = 1;
sum += side[i].w;
}
return sum;
}
IL int get_dep(int u)
{
if(dep[u] != -1) return dep[u];
if(!fa[u][0]) return dep[u] = 0;
return dep[u] = get_dep(fa[u][0]) + 1;
}
IL void lca()
{
int n2 = (n << 1) - 1;
memset(dep, -1, sizeof(dep));
for(int i = 1; i <= n2; ++i)
if(dep[i] == -1)
get_dep(i);
for(int i = 2; i <= n2; ++i)
lg[i] = lg[i >> 1] + 1;
for(int j = 1; j <= lg[n2]; ++j)
for(int i = 1; i <= n2; ++i)
if(fa[i][j - 1])
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
IL void swap_(int &x, int &y) { int tmp = x; x = y; y = tmp; }
IL int query(int x, int y)
{
if(dep[x] < dep[y]) swap_(x, y);
for(int t = dep[x] - dep[y]; t; t -= t & (-t))
x = fa[x][lg[t & (-t)]];
if(x == y) return x;
for(int i = lg[dep[x]]; i >= 0; --i)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
return fa[x][0];
}
IL void solve(ll sum)
{
ll det = -1, w;
for(int i = 1, p, x, y; i <= m; ++i)
if(!side[i].f)
{
x = side[i].u; y = side[i].v;
p = query(x, y);
w = side[i].w - weight[p];
if(w)
{
if(det == -1 || w < det) det = w;
}else
if(!w)
{
for(x = fa[x][0]; x != p; x = fa[x][0])
if(weight[x] != side[i].w && weight[x] > w)
w = weight[x];
for(y = fa[y][0]; y != p; y = fa[y][0])
if(weight[y] != side[i].w && weight[y] > w)
w = weight[y];
det = weight[p] - w;
}
}
printf("%lld\n", det + sum);
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= m; ++i)
{
side[i].u = read(); side[i].v = read(); side[i].w = read();
}
ll sum = kru();
lca();
solve(sum);
return 0;
}