图论——次小生成树
我们定义一个图的次小生成树(如果存在)为边权之和大于等于最小生成树的第二小生成树。
由于次小生成树和最小生成树之间只差一个边的改动,我们可以先求一边最小生成树,遍历所有未添加的边 ( u , v ) (u,v) (u,v),并计算路径 u → v u \to v u→v中的最大边权,用这个边去替换最大边。在所有方案中取最小即可。
这样求的生成树是不严格次小生成树,如果要求严格次小生成树,我们必须维护路径上最大和次大边权,在所有方案中取最小即可。可以通过树链剖分,倍增,ST表实现。
#include <bits/stdc++.h>
#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)
using namespace std;
int pos[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long, long> pll;
ll W;
const ll mod = 1e9 + 7;
pll CH(ll p[4])
{
sort(p, p + 4);
pll ans = make_pair(p[3], p[3]);
for (int i = 2; i >= 0; i--)
{
ans.second = p[i];
if (ans.second != ans.first)
break;
}
return ans;
}
int n, m;
struct Edge
{
int from;
int to;
ll w;
bool vis;
bool operator<(const Edge &o) const
{
return w < o.w;
}
} e[300005];
struct Edge2
{
int to;
int nxt;
ll w;
} e2[600005];
int tot = 0;
int head[100005];
void add(int u, int v, ll w)
{
tot++;
e2[tot].to = v;
e2[tot].nxt = head[u];
e2[tot].w = w;
head[u] = tot;
}
int pre[100005];
int find(int u)
{
return u == pre[u] ? u : pre[u] = find(pre[u]);
}
int siz[100005];
int dep[100005];
int wson[100005];
int fa[100005];
ll st[100005][30];
ll st1[100005][30];
void dfs1(int u, int r)
{
fa[u] = r;
siz[u] = 1;
dep[u] = dep[r] + 1;
for (int ne = head[u]; ne; ne = e2[ne].nxt)
{
int to = e2[ne].to;
if (to == r)
continue;
dfs1(to, u);
siz[u] += siz[to];
if (siz[to] > siz[wson[u]])
{
wson[u] = to;
}
}
}
int dfn[100005];
int top[100005];
int IDK;
void dfs2(int u, int r)
{
dfn[u] = ++IDK;
if (wson[u] != 0)
{
top[wson[u]] = top[u];
dfs2(wson[u], u);
}
for (int ne = head[u]; ne; ne = e2[ne].nxt)
{
int to = e2[ne].to;
if (to == r || to == wson[u])
continue;
top[to] = to;
dfs2(to, u);
}
}
void dfs3(int u, int r)
{
for (int ne = head[u]; ne; ne = e2[ne].nxt)
{
int to = e2[ne].to;
if (to == r)
continue;
st[dfn[to]][0] = e2[ne].w;
st1[dfn[to]][0] = e2[ne].w;
dfs3(to, u);
}
}
void buildST()
{
for (int e = 1; e < 30; e++)
{
for (int i = 1; i + (1 << e) - 1 <= n; i++)
{
ll p[4] = {st[i][e - 1], st1[i][e - 1],
st[i + (1 << (e - 1))][e - 1],
st1[i + (1 << (e - 1))][e - 1]};
pll ans = CH(p);
st[i][e] = ans.first;
st1[i][e] = ans.second;
}
}
}
int LOG[100005];
pll query(int l, int r)
{
int e = LOG[r - l + 1];
ll p[4] = {st[l][e], st1[l][e], st[r - (1 << e) + 1][e],
st1[r - (1 << e) + 1][e]};
pll ans = CH(p);
return ans;
}
pll LCA(int u, int v)
{
pll ans;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
{
swap(u, v);
}
pll c = query(dfn[top[u]], dfn[u]);
ll p[4] = {c.first, c.second, ans.first, ans.second};
pll k = CH(p);
ans.first = k.first;
ans.second = k.second;
u = fa[top[u]];
}
if (dep[v] > dep[u])
{
swap(u, v);
}
if (u == v)
return ans;
pll c = query(dfn[v] + 1, dfn[u]);
ll p[4] = {c.first, c.second, ans.first, ans.second};
pll k = CH(p);
ans.first = k.first;
ans.second = k.second;
return ans;
}
int main()
{
FR;
scanf("%d %d", &n, &m);
for (int i = 2; i < 100005; i++)
{
LOG[i] = LOG[i >> 1] + 1;
}
for (int i = 1; i <= n; i++)
{
pre[i] = i;
}
for (int i = 0; i < m; i++)
{
scanf("%d %d %lld", &e[i].from, &e[i].to, &e[i].w);
}
sort(e, e + m);
for (int i = 0; i < m; i++)
{
int ur = find(e[i].from);
int vr = find(e[i].to);
if (ur == vr)
continue;
W += e[i].w;
pre[ur] = vr;
add(e[i].from, e[i].to, e[i].w);
add(e[i].to, e[i].from, e[i].w);
e[i].vis = true;
}
dfs1(1, 1);
dfs2(1, 1);
dfs3(1, 1);
buildST();
ll ans = 999999999999999999;
for (int i = 0; i < m; i++)
{
if (!e[i].vis)
{
pll p = LCA(e[i].from, e[i].to);
if (p.first != e[i].w)
{
ans = min(ans, W - p.first + e[i].w);
}
if (p.second != e[i].w)
{
ans = min(ans, W - p.second + e[i].w);
}
}
}
printf("%lld", ans);
return 0;
}