您的位置:首页 > 其它

【NOIP 模拟题】最小生成树(Tarjan求桥)

2016-08-26 20:29 387 查看
                                   

                                       
                                                    

 

                                   


                                  【输出样例2】

              



—————————————————————————————————————————————————

【题解】【Tarjan缩点】

【首先通过Kruskal的思想来考虑,从边权小的开始,把不在同一并查集的点并查到一起】

【这样看来,如果当前图的边权都不同,那么最小生成树是唯一确定的,即要么是"any"要么是"none";那么我们需要考虑的就是有相同边权的图】

【按照kruskal的思想,先将边按边权排序,将边权相同的边放到一起处理,将不在同一个并查集里的两边,并查到一起,并用这些边建新图,并用Tarjan求桥,如果一条边是桥,那么就是"any",否则就是"at least one"】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct data{
int x,y,val;
int num;
}g[100010];//记录原始图
int f[100010],nm[100010],dfn[100010],low[100010];//f数组维护并查集;nm表示当前边在最小生成树中出现的可能性是怎样的;
int a[200010],nxt[200010],p[100010],w[200010],tot;//next数组存图
int n,m,cnt;
int tmp(data a,data b)
{
return a.val<b.val;
}
inline void add(int x,int y,int v)
{
tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot; w[tot]=v;
tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot; w[tot]=v;
}
int find(int x)//并查集
{
if(f[x]==x) return x;
f[x]=find(f[x]);
return f[x];
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++tot;
int u=p[x];
while(u)
{
if(a[u]==fa) {fa=0; u=nxt[u]; continue;}
if(!dfn[a[u]])
{
tarjan(a[u],x);
low[x]=min(low[a[u]],low[x]);
if(low[a[u]]>dfn[x]) nm[w[u]]=2;
}
else low[x]=min(dfn[a[u]],low[x]);
u=nxt[u];
}
}//tarjan求桥
int main()
{
freopen("mst.in","r",stdin);
freopen("mst.out","w",stdout);
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i) f[i]=i;
for(i=1;i<=m;++i) scanf("%d%d%d",&g[i].x,&g[i].y,&g[i].val),g[i].num=i;
sort(g+1,g+m+1,tmp);
i=1;cnt=0;
while(i<=m)
{
j=i;
while(g[i].val==g[j].val) ++j;//找出边权相同的区间
--j; tot=0;
for(int k=i;k<=j;++k)
{
int f1=find(g[k].x),f2=find(g[k].y);
if(f1==f2) continue;
nm[g[k].num]=1; dfn[f1]=dfn[f2]=0; p[f1]=p[f2]=0;
}//处理当期区间前重置、预处理
for(int k=i;k<=j;++k)
if(nm[g[k].num]==1)
{
int f1=find(g[k].x),f2=find(g[k].y);
add(f1,f2,g[k].num);
}//把当前情况下会加入最小生成树的边加入图中
for(int k=i;k<=j;++k)
if(!dfn[find(g[k].x)]) tarjan(g[k].x,0);//Tarjan求桥
for(int k=i;k<=j;++k)
{
int f1=find(g[k].x),f2=find(g[k].y);
if(f1!=f2) f[f1]=f2,cnt++;
}//把当前图中有的边,即联通两个联通块的边并查到一起
if(cnt==n-1) break;
i=j+1;
}
for(i=1;i<=m;++i)
{
if(!nm[i]) {printf("none\n"); continue;}
if(nm[i]==1) {printf("at least one\n"); continue;}
printf("any\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息