BZOJ1977: [BeiJing2010组队]次小生成树 Tree

来源:互联网 发布:龙珠激斗刷龙石软件 编辑:程序博客网 时间:2024/06/07 05:44

题目链接

严格次小生成树模板题。构建最小生成树后,枚举每一条非树边,找两顶点链间的最大边,若最大边和当前边边权向同,则找次大边,更新答案。

【代码】

#include <cstdio>#include <iostream>#include <queue>#include <vector>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#define N 100005#define M 200005#define INF 1000000001using namespace std;typedef long long ll;typedef pair<ll,ll> pa;int read(){    int x=0,f=1;char ch=getchar();    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}    return x*f;}ll ans;int n,m,cnt,mn=INF,mx1,mx2;int father[N];bool flag[300005];int b[M],p[N],nextedge[M],w[M];int deep[N],d1[N][17],d2[N][17],fa[N][17];class Edge{    public:        int x,y,z;}e[300005];void Add(int x,int y,int z){    cnt++;    b[cnt]=y;    nextedge[cnt]=p[x];    p[x]=cnt;    w[cnt]=z;}void Anode(int x,int y,int z){    Add(x,y,z);Add(y,x,z);}bool cmp(Edge a,Edge b){    return a.z<b.z;}void Input_Init(){    n=read();m=read();    for(int i=1;i<=m;i++)        e[i].x=read(),e[i].y=read(),e[i].z=read();    sort(e+1,e+1+m,cmp);}int findf(int x){    return father[x]==x?x:father[x]=findf(father[x]);}void Kruskal(){    int tot=0;    for(int i=1;i<=n;i++) father[i]=i;    for(int i=1;i<=m;i++)    {        int fx=findf(e[i].x),fy=findf(e[i].y);        if(fx!=fy)        {            father[fx]=fy;tot++;flag[i]=1;            ans+=e[i].z;Anode(e[i].x,e[i].y,e[i].z);            if(tot==n-1) break;        }    }}void dfs(int x,int f){    for(int i=1;i<=16;i++)    {        fa[x][i]=fa[fa[x][i-1]][i-1];        d1[x][i]=max(d1[x][i-1],d1[fa[x][i-1]][i-1]);        if(d1[x][i-1]==d1[fa[x][i-1]][i-1])            d2[x][i]=max(d2[x][i-1],d2[fa[x][i-1]][i-1]);        else        {            d2[x][i]=min(d1[x][i-1],d1[fa[x][i-1]][i-1]);            d2[x][i]=max(d2[x][i],d2[x][i-1]);            d2[x][i]=max(d2[x][i],d2[fa[x][i-1]][i-1]);        }    }    for(int i=p[x];i;i=nextedge[i])    {        int v=b[i];        if(v==f) continue;        fa[v][0]=x;        d1[v][0]=w[i];        deep[v]=deep[x]+1;        dfs(v,x);    }}void Update(int x,int i){    if(d1[x][i]>mx1)    {        mx2=mx1;        mx1=d1[x][i];    }    mx2=max(mx2,d2[x][i]);}void Work(int a){    mx1=0,mx2=0;    int x=e[a].x,y=e[a].y,z=e[a].z;    if(deep[x]<deep[y]) swap(x,y);    int t=deep[x]-deep[y];    for(int i=0;i<16;i++)    {        if((1<<i)&t)        {            Update(x,i);            x=fa[x][i];        }    }    for(int i=16;i>=0;i--)    {        if(fa[x][i]!=fa[y][i])        {            Update(x,i);            x=fa[x][i];            Update(y,i);            y=fa[y][i];        }    }    if(x!=y)    {        if(d1[x][0]>mx1)        {            mx2=mx1;            mx1=d1[x][0];        }        else if(d1[x][0]>mx2) mx2=d1[x][0];        if(d1[y][0]>mx1)        {            mx2=mx1;            mx1=d1[y][0];        }        else if(d1[y][0]>mx2) mx2=d1[y][0];    }    if(mx1!=z) mn=min(mn,z-mx1);    else mn=min(mn,z-mx2);}void Solve(){    for(int i=1;i<=m;i++)    {        if(flag[i]) continue;        Work(i);    }    printf("%lld\n",ans+mn);}int main(){    Input_Init();    Kruskal();    dfs(1,0);    Solve();    return 0;}
0 0