CodeForces Round #111 Div.2 problem D 160D
来源:互联网 发布:阿里云os是什么 编辑:程序博客网 时间:2024/06/05 14:53
/*题意:最小生成树and tarjan.给定一个简单无向连通图G(v,e),他的最小生成树为T(不一定唯一),对于图中的任意一条边,如果它不可能在T中,输出none,如果它一定在T中,输出any如果它可能在T中,输出at least one题解:只有有相同权值的边的时候才可能出现at least one的情况G的点集为N如果 e1,e2,e3...的权值相同,在N中以克鲁斯卡尔算法的方式加入所有边权小于e1的边(这个时候出现一些联通分量)把这些连通分量抽象成点(这一点并查集可以做到),那么出现一个新的图Y(一定无环),这Y中也以鲁斯卡尔算法的方式加入e1,e2, e3....(没加入的肯定是连通分量内部的边,必然为none),如果出现环(包括两条重边构成的环),那么这些加入的e必然是at least one,因为可以通过破环把其中之一剔除出最小生成树,其他的必然为any(这些是新图Y的割边)。*/#include <iostream>#include <algorithm>#include <map>#include <set>#include <cstdio>#include <vector>using namespace std;const int N=100009;struct Edge { int u,v,w,ans,id; bool operator<(const Edge &y) const { return this->w<y.w; }}e[N];int n,m;bool cmp(Edge x,Edge y){ return x.id<y.id;}map<int,vector<int> > v,mark;map<int,int> vis,first;set<int> Set;int fa[N],tim;char res[][20]={"none","at least one","any"};int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]);}void tarjan(int x){ if(vis[x])return ; vis[x]=first[x]=++tim; for(int i=0;i<v[x].size();i++) { int y=v[x][i]; int Mark=mark[x][i]; if(Set.count(Mark))continue;//记录边是否访问过,漏掉重边构成的环 Set.insert(Mark); tarjan(y); if(vis[y]<vis[x]) { vis[x]=vis[y]; } if(vis[y]>first[x])//找到割边 { e[Mark].ans=2; } }}int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<=n;i++)fa[i]=i; for(int i=0;i<m;i++) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); e[i].ans=0; e[i].id=i; } sort(e,e+m); for(int i=0;i<m;) { int s=i,x,y; while(i<m&&e[i].w==e[s].w) i++;//挑出一段相同边权的边 v.clear(); vis.clear(); mark.clear(); Set.clear(); for(int j=s;j<i;j++) { x=find(e[j].u); y=find(e[j].v); if(x==y)continue; e[j].ans=1; v[x].push_back(y); v[y].push_back(x); mark[x].push_back(j);//记录两点间边的编号 mark[y].push_back(j); } for(map<int,vector<int> >::iterator it=v.begin();it!=v.end();it++) { tarjan(it->first); } for(int j=s;j<i;j++) { x=find(e[j].u); y=find(e[j].v); if(x==y)continue; fa[x]=y; } } sort(e,e+m,cmp); for(int i=0;i<m;i++) { printf("%s\n",res[e[i].ans]); } } return 0;}