洛谷-4180 【模板】严格次小生成树[BJWC2010]

题目描述
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) ∑ e ∈ E M v a l u e ( e ) &lt; ∑ e ∈ E S v a l u e ( e ) \sum_{e \in E_M}value(e)&lt;\sum_{e \in E_S}value(e) eEMvalue(e)<eESvalue(e)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
输入格式
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
输出格式
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

输入输出样例
输入 #1
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

输出 #1
11

说明/提示
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
typedef pair<ll,ll> pii;
template<class T>ll chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>ll chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
ll read(){
    ll s=0,base=1;
    char c;
    while(!isdigit(c=getchar()))if(c=='-')base=-base;
    while(isdigit(c)){s=s*10+(c^48);c=getchar();}
    return s*base;
}
char WritellBuffer[1024];
template<class T>void write(T a,char end){
    ll cnt=0,fu=1;
    if(a<0){putchar('-');fu=-1;}
    do{WritellBuffer[++cnt]=fu*(a%10)+'0';a/=10;}while(a);
    while(cnt){putchar(WritellBuffer[cnt]);--cnt;}
    putchar(end);
}
struct edge{
    ll u,v,w;
};
edge e[333333];
ll fa[102424],use[333333];
ll beg[102424],to[204848],lst[204848],w[204848],dis[102424],en;
ll f[102424][20],mx1[102424][20],mx2[102424][20];
ll e_cmp(edge a,edge b){
    return a.w<b.w;
}
ll find(ll x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void add(ll u,ll v,ll wa){
    to[++en]=v;
    lst[en]=beg[u];
    beg[u]=en;
    w[en]=wa;
}
void dfs(ll x,ll fa){
    f[x][0]=fa;
    dis[x]=dis[fa]+1;
    for(ll i=beg[x];i;i=lst[i])if(to[i]!=fa){
        dfs(to[i],x);
    }
    else mx1[x][0]=w[i];
}
void upd(ll &mx1,ll &mx2,ll mxx1,ll mxx2){
    ll nm=max(mx1,mxx1),inm=0;
    if(mx1==nm)chkmax(inm,mx2);
    else chkmax(inm,mx1);
    if(mxx1==nm)chkmax(inm,mxx2);
    else chkmax(inm,mxx1);
    mx1=nm;
    mx2=inm;
}
void build(ll n,ll t){
    for(ll j=1;j<=t;++j)for(ll i=1;i<=n;++i){
        f[i][j]=f[f[i][j-1]][j-1];
        mx1[i][j]=mx1[i][j-1];
        mx2[i][j]=mx2[i][j-1];
        upd(mx1[i][j],mx2[i][j],mx1[f[i][j-1]][j-1],mx2[f[i][j-1]][j-1]);
    }
}
ll _lca(edge e){
    ll u=e.u,v=e.v,w=e.w,max1=0,max2=0;
    if(dis[u]<dis[v])swap(u,v); 
    for(ll i=18;~i;--i){
        if(dis[u]-(1<<i)>=dis[v]){
            upd(max1,max2,mx1[u][i],mx2[u][i]);
            u=f[u][i];
        }
    }
    for(ll i=18;~i;--i){
        if(f[u][i]!=f[v][i]){
            upd(max1,max2,mx1[u][i],mx2[u][i]); 
            upd(max1,max2,mx1[v][i],mx2[v][i]); 
            u=f[u][i];
            v=f[v][i];
        }
    }
    if(u!=v){
        ll i=0; 
        upd(max1,max2,mx1[u][i],mx2[u][i]); 
        upd(max1,max2,mx1[v][i],mx2[v][i]); 
        u=f[u][i];
        v=f[v][i];
    }
    if(max1==w)if(max2)return w-max2;else return 0x3f3f3f3f3f3f3f3f;
    else return w-max1;
}
int main(){
    ll n,m;
    n=read();m=read();
    for(ll i=1;i<=m;++i){ 
        e[i].u=read();
        e[i].v=read();
        e[i].w=read();
    }
    sort(e+1,e+m+1,e_cmp);
    for(ll i=1;i<=n;++i)fa[i]=i;
    ll q=n;
    ll sum=0,ans=0x3f3f3f3f3f3f3f3f;
    for(ll i=1;q>1;++i)if(find(e[i].u)!=find(e[i].v)){
        add(e[i].v,e[i].u,e[i].w);
        add(e[i].u,e[i].v,e[i].w);
        sum+=e[i].w;
        fa[find(e[i].u)]=find(e[i].v);  
        use[i]=1;
        --q;
    }
    dfs(1,0);
    build(n,18);
    for(ll i=1;i<=m;++i)if(!use[i]){
        chkmin(ans,sum+_lca(e[i]));
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值