[BJWC2010]严格次小生成树

题意

Here

思考

严格次小生成树,就是求出严格的次小生成树~

次小生成树其实是很好求的,枚举每一个没选的边 ((u, v, d)),用它来替换 (u, v) 间的最大边即可,但是存在没选的边与要替换的边权值相等,那么就会出现次小生成树的权值等于最小生成树的权值,这样就不“严格”了。

当然求严格次小生成树也不是很难,只要利用倍增数组(边权塞进点里)记录点对间边权的最大值与严格次大值即可~细节看代码

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x * f;
}
const ll oo = 0x3f3f3f3f3f3f3f3f;
const ll M = 300030;
const ll N = 100010;
struct node{
    ll nxt, from, to, dis;
}edge[M << 1], E[M];
ll head[N], num = 0;
void build(ll from, ll to, ll dis){
    edge[++num].nxt = head[from];
    edge[num].to = to;
    edge[num].from = from;
    edge[num].dis = dis;
    head[from] = num;
}
bool cmp(node a, node b){ return a.dis < b.dis; }
ll d[N], f[N][20], MAX1[N][20], MAX2[N][20];
void dfs(ll u, ll fa){
    for(ll i=head[u]; i; i=edge[i].nxt){
        ll v = edge[i].to;
        if(v == fa) continue;
        f[v][0] = u; d[v] = d[u] + 1; MAX1[v][0] = edge[i].dis; MAX2[v][0] = -oo;
        dfs(v, u);
    }
}
ll lca(ll u, ll v){
    if(d[u] < d[v]) swap(u, v);
    for(ll i=18; i>=0; i--) if(d[f[u][i]] >= d[v]) u = f[u][i];
    if(u == v) return u;
    for(ll i=18; i>=0; i--) if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    return f[u][0];
}
ll fa[N], vis[M], n, m, sum;
ll findx(ll x){
    if(fa[x] == x) return x;
    return fa[x] = findx(fa[x]);
}
ll kruskal(){
    ll sum = 0;
    sort(E+1, E+m+1, cmp); ll js = 0;
    for(ll i=1; i<=n; i++) fa[i] = i;
    for(ll i=1; i<=m; i++){
        ll u = E[i].from, v = E[i].to;
        u = findx(u), v = findx(v);
        if(u == v) continue;
        fa[u] = v; build(E[i].from, E[i].to, E[i].dis); build(E[i].to, E[i].from, E[i].dis);
        vis[i] = 1; js ++;
        sum += E[i].dis;
        if(js == n-1) return sum;
    }
    return -1;
}
ll query(ll u, ll v, ll now){
    if(d[u] < d[v]) swap(u, v); ll ans = -oo;
    for(ll i=18; i>=0; i--){
        if(d[f[u][i]] >= d[v]){
            if(now == MAX1[u][i]){
                ans = max(ans, MAX2[u][i]);
            }
            else ans = max(ans, MAX1[u][i]);
            u = f[u][i];
        }
    }
    return ans;
}
int main(){
    n = read(), m = read();
    for(ll i=1; i<=m; i++){
        E[i].from = read(), E[i].to = read(), E[i].dis = read();
    }
    sum = kruskal();
    d[1] = 1; MAX2[1][0] = -oo;
    dfs(1, 0);
    for(ll j=1; j<=18; j++)
        for(ll i=1; i<=n; i++){
            f[i][j] = f[f[i][j-1]][j-1];
            MAX1[i][j] = max(MAX1[i][j-1], MAX1[f[i][j-1]][j-1]);
            MAX2[i][j] = max(MAX2[i][j-1], MAX2[f[i][j-1]][j-1]);
            if(MAX1[i][j-1] > MAX1[f[i][j-1]][j-1]){
                MAX2[i][j] = max(MAX2[i][j], MAX1[f[i][j-1]][j-1]);
            }
            else if(MAX1[i][j-1] < MAX1[f[i][j-1]][j-1]){
                MAX2[i][j] = max(MAX1[i][j-1], MAX2[i][j]);
            }
        }
    ll ans = 0x3f3f3f3f3f3f3f3f;
    for(ll i=1; i<=m; i++){
        if(!vis[i]){
            ll u = E[i].from, v = E[i].to, d = E[i].dis;
            ll LCA = lca(u, v);
            ll Q = query(u, LCA, d);
            ll Q2 = query(v, LCA, d);<< endl;
            Q = max(Q, Q2);
            ans = min(ans, sum - Q + E[i].dis);
        }
    }
    cout << ans;
    return 0;
}

总结

主要注意一下怎样记录严格次大值,怎样转移倍增数组(细节见代码,分类讨论要注意一下,我之前转移错了 (QWQ)

原文地址:https://www.cnblogs.com/alecli/p/9911414.html