【BJOI2010】次小生成树-最小生成树+倍增LCA

来源:互联网 发布:矩阵行列式的计算 编辑:程序博客网 时间:2024/06/05 01:45

题目大意:给定一个带边权的无向图,求该图中严格次小生成树上的边权和。
做法:本题需要用到最小生成树+倍增LCA。
所谓严格次小生成树,就是边权和严格大于最小生成树的边权和最小的生成树。我们求非严格次小生成树时,是对于每一条不在最小生成树中的边,将它加入后,再从构成的环中去掉一个除了它之外最大的边,所有这样构成的树中边权和最小的就是次小生成树了。然而这次要求严格次小生成树,这就意味着它的边权和必须严格大于最小生成树,所以对于每条不在最小生成树中的边,如果它和它两个端点在最小生成树上路径中最大边相等,就要用严格次大的边来作为被删掉的边。树上两点间严格次大的边权可以用倍增LCA来找,具体的预处理方法这里解释不清,请看代码吧。这样一来,时间复杂度是O(MlogN)的,可以通过此题。
以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#define ll long longusing namespace std;int n,m;ll totalsum,ans=2000000000;int first[100010],tot=0,f[100010];int fa[100010][21]={0},mx[100010][21]={0},smx[100010][21]={0},dep[100010]={0};struct edge {int v,next;ll d;} e[200010];struct forsort {int x,y;ll z;} a[300010];bool chosen[300010]={0};bool cmp(forsort a,forsort b){    return a.z<b.z;}int find(int x){    int r=x,i=x,j;    while(f[r]!=r) r=f[r];    while(i!=r)    {        j=f[i];        f[i]=r;        i=j;    }    return r;}void merge(int a,int b){    int fa=find(a),fb=find(b);    f[fa]=fb;}void insert(forsort a){    e[++tot].v=a.y,e[tot].d=a.z,e[tot].next=first[a.x],first[a.x]=tot;    e[++tot].v=a.x,e[tot].d=a.z,e[tot].next=first[a.y],first[a.y]=tot;}void kruskal(){    sort(a+1,a+m+1,cmp);    int now=0;totalsum=0;    for(int i=1;i<=n;i++) f[i]=i;    for(int i=1;i<=m;i++)    {        if (now==n-1) break;        int fx=find(a[i].x),fy=find(a[i].y);        if (fx!=fy)        {            now++;            totalsum+=a[i].z;            chosen[i]=1;            insert(a[i]);            merge(a[i].x,a[i].y);        }    }}void dfs(int v){    smx[v][0]=-1;    for(int i=first[v];i;i=e[i].next)        if (e[i].v!=fa[v][0])        {            fa[e[i].v][0]=v;            mx[e[i].v][0]=e[i].d;            dep[e[i].v]=dep[v]+1;            dfs(e[i].v);        }}void calc(ll &val1,ll val2,ll &val3,ll val4){    ll v1,v2;    v1=max(val1,val2);    if (val3>=val2) v2=val3;    else if (val4>=val1) v2=val4;         else if (val1!=val2) v2=min(val1,val2);              else v2=max(val3,val4);    val1=v1,val3=v2;}ll query(ll d,int x,int y){    ll mxx=0,smxx=0;    if (dep[x]<dep[y]) swap(x,y);    for(int i=20;i>=0;i--)        if (dep[fa[x][i]]>=dep[y])        {            calc(mxx,mx[x][i],smxx,smx[x][i]);            x=fa[x][i];        }    if (x!=y)    {        for(int i=20;i>=0;i--)            if (fa[x][i]!=fa[y][i])            {                calc(mxx,mx[x][i],smxx,smx[x][i]);                calc(mxx,mx[y][i],smxx,smx[y][i]);                x=fa[x][i],y=fa[y][i];            }        calc(mxx,mx[x][0],smxx,smx[x][0]);        calc(mxx,mx[y][0],smxx,smx[y][0]);    }    if (d==mxx&&smxx!=-1) return smxx;    else return mxx;}void work(){    dfs(1);    for(int i=1;i<=20;i++)        for(int j=1;j<=n;j++)        {            fa[j][i]=fa[fa[j][i-1]][i-1];            ll val1=mx[j][i-1],val2=mx[fa[j][i-1]][i-1],val3=smx[j][i-1],val4=smx[fa[j][i-1]][i-1];            mx[j][i]=max(val1,val2);            if (val3>=val2) smx[j][i]=val3;            else if (val4>=val1) smx[j][i]=val4;                 else if (val1!=val2) smx[j][i]=min(val1,val2);                      else smx[j][i]=max(val3,val4);        }    for(int i=1;i<=m;i++)        if (!chosen[i])        {            ll val=query(a[i].z,a[i].x,a[i].y);            if (val!=-1) ans=min(ans,a[i].z-val);        }}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)        scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].z);    kruskal();    work();    printf("%lld",totalsum+ans);    return 0;}
阅读全文
0 0
原创粉丝点击