【YBTOJ】【Luogu P4180】[BJWC2010]严格次小生成树
【YBTOJ】【Luogu P4180】[BJWC2010]严格次小生成树:
链接:
题目大意:
求次小生成树。
正文:
本题有一个至关重要的性质:次小生成树只与最小生成树有一边之差。
那么只用在最小生成树中加边,在环中的边里找一个最大的边减去。将所有这样的操作取最大,就是非严格最小生成树。有个“非”,是因为最大的边权可能与加边的边权相等,所以遇到这种情况要取次大值。
倍增操作求值就能解决问题了。
代码:
const int N = 1e5 + 10, M = 3e5 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
// main
int n, m;
int logN;
ll ans = 1e17, sum;
// edge
struct Edge
{
struct edge
{
int from, to, nxt;ll val; bool t;
bool operator < (edge &a) const
{
return val < a.val;
}
}e[M << 1];
int head[N], tot;
void add(int u, int v, ll w)
{
e[++tot] = (edge) {u, v, head[u], w, 1}, head[u] = tot;
}
} g, t;
// Kruskal
int fa[N];
int Find(int k) {return k == fa[k]? k: fa[k] = Find(fa[k]);}
void Kruskal()
{
sort (g.e + 1, g.e + 1 + m);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++)
{
int u = Find(g.e[i].from), v = Find(g.e[i].to);
if (u == v) continue;
fa[u] = v;
sum += g.e[i].val;
t.add(g.e[i].from, g.e[i].to, g.e[i].val);
t.add(g.e[i].to, g.e[i].from, g.e[i].val);
g.e[i].t = 0;
}
}
// LCA
int dep[N];
bool vis[N];
int f[N][30];
ll Max[N][30], sMax[N][30];
queue <int> q;
void BFS(int root)
{
while(!q.empty()) q.pop();
q.push(root);
dep[root] = 1;
while(!q.empty())
{
int u = q.front(); q.pop();
vis[u] = 1;
for (int i = t.head[u]; i; i = t.e[i].nxt)
{
int v = t.e[i].to;
if (vis[v]) continue;
dep[v] = dep[u] + 1;
Max[v][0] = t.e[i].val;
sMax[v][0] = -1e17;
f[v][0] = u;
for (int j = 1; j <= logN; j++)
{
f[v][j] = f[f[v][j - 1]][j - 1];
sMax[v][j] = max(sMax[v][j - 1], sMax[f[v][j - 1]][j - 1]);
Max[v][j] = max(Max[v][j - 1], Max[f[v][j - 1]][j - 1]);
if (Max[v][j - 1] > Max[f[v][j - 1]][j - 1])
sMax[v][j] = max(sMax[v][j], Max[f[v][j - 1]][j - 1]);
if (Max[v][j - 1] < Max[f[v][j - 1]][j - 1])
sMax[v][j] = max(sMax[v][j], Max[v][j - 1]);
}
q.push(v);
}
}
}
int LCA(int u, int v)
{
if (dep[u] > dep[v]) u ^= v ^= u ^= v;
for (int j = logN; ~j; j--)
if(dep[f[v][j]] >= dep[u])
v = f[v][j];
if (u == v) return u;
for (int j = logN; ~j; j--)
if(f[v][j] != f[u][j])
v = f[v][j], u = f[u][j];
u = f[u][0], v = f[v][0];
return u;
}
ll solve (int u, int v, ll k)
{
ll ans = -1e17;
for (int j = logN; ~j; j--)
if(dep[f[u][j]] >= dep[v])
{
if (k != Max[u][j]) ans = max(ans, Max[u][j]);
else ans = max(ans, sMax[u][j]);
u = f[u][j];
}
return ans;
}
// main()
int main()
{
n = Read(), m = Read();
logN = log2(m) + 1;
for (int i = 1; i <= m; i++)
{
int u = Read(), v = Read(); ll w = Read();
g.add(u, v, w);
}
Kruskal();
BFS(1);
for (int i = 1; i <= m; i++)
{
if (!g.e[i].t) continue;
int u = g.e[i].from, v = g.e[i].to;
ll tmp = sum, w = g.e[i].val;
int c = LCA(u, v);
tmp = tmp + w - max(solve (u, c, w), solve (v, c, w));
ans = min(ans, tmp);
}
printf ("%lld\n", ans);
return 0;
}