BZOJ 1977: [BeiJing2010组队]次小生成树 Tree 倍增 最小生成树

好吧我太菜了又调了一晚上。。。QAQ


先跑出最小生成树,标记树边,再用树上倍增的思路,预处理出:

  f[u][i] :距离u为2^i的祖先

  h[u][i][0/1] :距u点在2^i范围内的最长边和次长边

然后枚举每一条非树边(u,v),会与原先的最小生成树构成一个环,而之前预处理出的数据可以快速找到(u,v)在最小生成树上的最大和次大边,来更新答案

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define int long long
#define R register int
using namespace std;
inline int g() {
    R ret=0,fix=1;register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
struct node{int u,v,w;bool operator <(const node & y) const { return w<y.w;}}a[600010];
struct edge{int v,w,nxt;}e[200010];
int n,m,cnt,mx,mmx,mn=0x3f3f3f3f,ans,tot; 
int fir[100010],f[100010][20],h[100010][20][2],fa[100010],d[100010];
bool tr[600010];
inline void add(int u,int v,int w) {e[++cnt].v=v,e[cnt].nxt=fir[u],e[cnt].w=w,fir[u]=cnt;}
inline void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);}
inline int getf(int x) {return x==fa[x]?x:fa[x]=getf(fa[x]);}
inline void dfs(int u,int fa) {
    for(R i=fir[u];i;i=e[i].nxt) {
        R v=e[i].v; if(v==fa) continue;
        f[v][0]=u,h[v][0][0]=e[i].w,d[v]=d[u]+1;
        for(R i=1;d[v]>=(1<<i);++i) {
            f[v][i]=f[f[v][i-1]][i-1]; 
            h[v][i][0]=max(h[v][i-1][0],h[f[v][i-1]][i-1][0]);
            if(h[v][i-1][0]==h[f[v][i-1]][i-1][0]) 
                h[v][i][1]=max(h[v][i-1][1],h[f[v][i-1]][i-1][1]);
            else {
                h[v][i][1]=min(h[v][i-1][0],h[f[v][i-1]][i-1][0]);
                h[v][i][1]=max(h[v][i-1][1],h[v][i][1]);
                h[v][i][1]=max(h[v][i][1],h[f[v][i-1]][i-1][1]);
            }
        } dfs(v,u);
    }
}
inline int lca(int u,int v) {
    if(d[u]<d[v]) swap(u,v); R lim=log2(d[u])+1; 
    for(R j=lim;j>=0;--j) if(d[f[u][j]]>=d[v]) u=f[u][j];
    if(u==v) return u; 
    for(R j=lim;j>=0;--j) if(f[u][j]!=f[v][j]) u=f[u][j],v=f[v][j];
    return f[u][0];
}
inline void calc(int u,int fa,int w) {
    mx=0,mmx=0; R lim=log2(d[u]-d[fa])+1;
    for(R i=lim;i>=0;--i) if(d[u]-d[fa]>=(1<<i)){
        if(h[u][i][1]>mx) mmx=mx,mx=h[u][i][0];
        mx=max(h[u][i][0],mx); mmx=max(mmx,h[u][i][1]),u=f[u][i];
    }
    if(mx!=w) mn=min(mn,w-mx); else mn=min(mn,w-mmx);
}
signed main() {
    n=g(),m=g();
    for(R i=1;i<=n;++i) fa[i]=i;
    for(R i=1;i<=m;++i) a[i].u=g(),a[i].v=g(),a[i].w=g();
    sort(a+1,a+m+1);
    for(R i=1;i<=m&&tot<n;++i) {
        R uf=getf(a[i].u),vf=getf(a[i].v);
        if(uf==vf) continue;
        fa[uf]=vf; ans+=a[i].w; tr[i]=true;
        ins(a[i].u,a[i].v,a[i].w); ++tot;
    } dfs(1,0);
    for(R i=1;i<=m;++i) if(!tr[i]) {
        R L=lca(a[i].u,a[i].v);
        calc(a[i].u,L,a[i].w); calc(a[i].v,L,a[i].w);
    } printf("%lld\n",ans+mn);
}

2019.04.09

猜你喜欢

转载自www.cnblogs.com/Jackpei/p/10674469.html