BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

严格次小生成树。一开始没有特批一圈都相等的情况,一直WA,十分难受。

先生成最小生成树,枚举每条非树边,连上它构成一个环,拆掉环上树边中最大的一条(若和该边相等则次大的一条)换上这条。

用倍增维护一条链上的最大边和次大边,倍增跑lca同时找出环上最大边和次大边,看能否更新答案。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
typedef long long LL;
using namespace std;
const int maxn=100000+299;
const int maxm=300000*2+299;
int upp=0,tot,n,m,k,ecnt,fir[maxn],nxt[maxm],to[maxm],vis[maxn],fa[maxn];
int f[maxn][32],R[maxn]; 
LL st[maxn][32],stc[maxn][32],ans,rem,rec,val[maxm],anspre;
struct edge{
    int u,v,w,is;
    friend bool operator <(const edge&A,const edge&B) {
        return A.w<B.w;
    }
}e[maxm];
void add(int u,int v,int w) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=(LL)w;
    nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=(LL)w;
}
void init() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) 
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
}
int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
void kruskal() {
    sort(e+1,e+m+1);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) {
        int u=e[i].u,v=e[i].v;
        int fu=find(u),fv=find(v);
        if(fu!=fv) {
            e[i].is=1;
            anspre+=e[i].w;
            add(u,v,e[i].w);
            tot++;
            if(tot==n-1) break;
            fa[fu]=fv;
        }
    }
}
void dfs(int x,int ff) {
    f[x][0]=ff; R[x]=R[ff]+1;
    for(int i=fir[x];i;i=nxt[i]) if(to[i]!=ff){
        st[to[i]][0]=val[i];
        stc[to[i]][0]=val[i];
        dfs(to[i],x);
    }
}
void make_st() {
    for(int i=1;i<=30;i++)
        for(int j=1;j<=n;j++) {
            f[j][i]=f[f[j][i-1]][i-1];
            int u=st[j][i-1],v=st[f[j][i-1]][i-1];
            st[j][i]=max(u,v);    
            if(u&&v&&u!=v) stc[j][i]=min(u,v);
            if(stc[j][i-1]) stc[j][i]=max(stc[j][i],stc[j][i-1]);
            if(stc[f[j][i-1]][i-1]) stc[j][i]=max(stc[j][i],stc[f[j][i-1]][i-1]);
        }
}
int swapp(LL x,LL &zd,LL &cd) {
    if(x==zd) return 0;
    if(x>zd) {
        cd=max(cd,zd);
        zd=x;
    }
    else cd=max(cd,x);
}
int lca(int x,int y) {
    rem=0,rec=0;
    if(R[x]<R[y]) swap(x,y);
    for(int i=30;i>=0;i--) {
        if(R[f[x][i]]>=R[y]) { 
            if(rem&&st[x][i]>rem) rec=max(rem,rec);
            rem=max(rem,st[x][i]);
            if(i!=0) rec=max(rec,stc[x][i]);
            x=f[x][i]; 
        }
    }
    if(x==y) return 1;
    for(int i=30;i>=0;i--) {
        if(f[x][i]!=f[y][i]) {
            swapp(stc[y][i],rem,rec);
            swapp(stc[x][i],rem,rec);
            swapp(st[x][i],rem,rec);
            swapp(st[y][i],rem,rec);
            x=f[x][i]; y=f[y][i];
        }
    }
    swapp(stc[y][0],rem,rec);
    swapp(stc[x][0],rem,rec);
    swapp(st[x][0],rem,rec);
    swapp(st[y][0],rem,rec);
    return 1;
}
void work() {
    ans=1e18;
    for(int i=1;i<=m;i++) if(e[i].is!=1){
        int x=e[i].u,y=e[i].v;
        lca(x,y);
        if(e[i].w!=rem) ans=min(ans,anspre-rem+e[i].w);
        else if(e[i].w!=rec) ans=min(ans,anspre-rec+e[i].w);
    }
    printf("%lld\n",ans);
}
int main()
{
    init();
    kruskal();
    dfs(1,0);
    make_st();
    work();
    return 0;
}
View Code

 

posted @ 2017-09-20 22:00  啊宸  阅读(222)  评论(0编辑  收藏  举报