/*
题意:
最小生成树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;
}
CodeForces Round #111 Div.2 problem D 160D
最新推荐文章于 2018-07-04 23:21:59 发布