[CF160D](Edges in MST)
题意:给你一个无向带权图,求每一条边:
1.在每个最小生成树中.
2.在部分最小生成树中.
3.不在任何一个最小生成树中.
solution:
建立并查集
将每条边按边权从小到大排序:
如果一条边的两端都在同一联通块中则该边为none
但是any 怎么区分呢
这就要用到割边的知识
我们把每组权相等的边拿出来
重新建图 (tips:保留原有的联通块(连通块以其祖先标记)
tarjan跑割边,此时割边即为any
否则是at least one
code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define N 200005 using namespace std; struct zero{ int nxt,to,id; }edge[N<<1]; int head[N],tot=0; void add_edge(int a,int b,int c){ edge[++tot]=(zero){head[a],b,c}; head[a]=tot; } struct sett{ int u,v,w,id; }lib[N]; int n,m,f[N],ans[N]; int dfn[N],idx=0; int tarjan(int x,int pre){ int lowu=dfn[x]=++idx; for(int i=head[x];i!=-1;i=edge[i].nxt){ int to=edge[i].to; if(!dfn[to]){ int lowv=tarjan(to,i); if(lowv>=dfn[to])ans[edge[i].id]=1; lowu=min(lowu,lowv); } else if(dfn[to]<dfn[x]&&i!=(pre^1))lowu=min(lowu,dfn[to]); } return lowu; } bool cmp(sett a,sett b){return a.w<b.w;} int find(int k){return (f[k]==k)?k:f[k]=find(f[k]);} int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&lib[i].u,&lib[i].v,&lib[i].w); lib[i].id=i; } sort(lib+1,lib+m+1,cmp); for(int i=1;i<=m;i++)f[i]=i; int last; for(int i=1;i<=m;i++){ last=i; while(last<=m&&lib[last].w==lib[i].w)last++;last--; for(int r=i;r<=last;r++){ lib[r].u=find(lib[r].u); lib[r].v=find(lib[r].v); if(lib[r].u==lib[r].v){ ans[lib[r].id]=-1; continue; } dfn[lib[r].u]=dfn[lib[r].v]=0; head[lib[r].u]=head[lib[r].v]=-1; } idx=0;tot=-1; for(int r=i;r<=last;r++) if(lib[r].u!=lib[r].v) add_edge(lib[r].u,lib[r].v,lib[r].id), add_edge(lib[r].v,lib[r].u,lib[r].id); for(int r=i;r<=last;r++) if(!dfn[lib[r].u])tarjan(lib[r].u,-1); for(int r=i;r<=last;r++){f[find(lib[r].u)]=find(lib[r].v);} i=last; } for(int i=1;i<=m;i++){ if(ans[i]==-1)puts("none"); else if(ans[i]==1)puts("any"); else puts("at least one"); } }