CF160D Edges in MST
首先考虑不可能构成最小生成树的边。我们先按边权排序,对于一条边 \(e\),我们将所有边权严格小于它的边相连,此时 \(n\) 个点构成了数个联通块,如果这条边两端的端点 \(u,v\) 已经在一个联通块里了,说明这条边根本用不上,也就是这条边是不可能构成最小生成树的边。
接下来考虑肯定在最小生成树里面的边。对于一条边 \(e\),我们依然将所有边权严格小于它的边相连,然后考虑所有边权和他相等的边,将其他边权和他相等的边相连,如果此时 \(e\) 的两端仍然不在一个联通块内,说明 \(e\) 是肯定在最小生成树里面的边。
但是如果暴力模拟的复杂度是 \(O(m^2)\) 的。通过观察可知,其实 \(e\) 就是原图中的割边。所以我们可以通过 Tarjan 算法求出割边。但是这样复杂度仍然不能保证,这样我们的复杂度是 \(w\times m\) 的。 \(w\) 表示不同边权的数量。考虑继续优化。
假设我们现在在研究边权为 \(w\) 的边集 \(e\)。我们发现,我们所求的割边是要在边集 \(e\) 中的,如果每一次我们 Tarjan 的复杂度能够达到 \(O(siz(e))\) 那么总时间复杂度就变成了 \(O(m)\)。所以,可以将所有边权小于 \(w\) 的边相连,并将所形成的联通块缩点。那么我们对边集 \(e\) 进行处理时,复杂度就变成了 \(O(siz(e))\)。
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e5+5;
int n,m,f[MAXN],color[MAXN],ans[MAXN],dfn[MAXN],low[MAXN],totdfn;
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
struct edge
{
int u,v,w,id;
bool operator < (const edge&x)const
{
return w<x.w;
}
}edge[MAXN];
struct E
{
int to,id;
};
vector <E> e[MAXN];
void add(int i)
{
int u=find(edge[i].u),v=find(edge[i].v),id=edge[i].id;
dfn[u]=dfn[v]=0;
e[u].push_back(E{v,id});
e[v].push_back(E{u,id});
}
void Merge(int u,int v)
{
f[v]=u;
e[u].clear();e[v].clear();
}
void dfs(int p,int fa)
{
dfn[p]=low[p]=++totdfn;
bool flag=0;
for(int i=0;i<e[p].size();++i)
{
int to=e[p][i].to;
if(to==fa&&!flag)
{
flag=1;
continue;
}
if(!dfn[to])
{
dfs(to,p);
if(low[to]>dfn[p]) ans[e[p][i].id]=1;
low[p]=min(low[p],low[to]);
}
else low[p]=min(low[p],dfn[to]);
}
}
int main()
{
// freopen("mstagain.in","r",stdin);
// freopen("mstagain.out","w",stdout);
scanf("%d %d",&n,&m);
int MX=0;
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
edge[i].u=u,edge[i].v=v,edge[i].w=w;
MX=max(MX,w);
edge[i].id=i;
}
for(int i=1;i<=n;++i) f[i]=i;
sort(edge+1,edge+1+m);
int now=1;
while(now<=m)
{
int p=now;
while(p<m&&edge[p+1].w==edge[p].w)
++p;
for(int i=now;i<=p;++i)
{
if(find(edge[i].u)==find(edge[i].v))
ans[edge[i].id]=2;
else add(i);
}
for(int i=now;i<=p;++i)
{
if(find(edge[i].u)==find(edge[i].v)) continue;
if(!dfn[find(edge[i].u)]) dfs(find(edge[i].u),0);
if(!dfn[find(edge[i].v)]) dfs(find(edge[i].v),0);
}
for(int i=now;i<=p;++i)
if(find(edge[i].u)!=find(edge[i].v)) Merge(find(edge[i].u),find(edge[i].v));
now=p+1;
}
for(int i=1;i<=m;++i)
{
if(ans[i]==1) printf("any\n");
else if(ans[i]==0) printf("at least one\n");
else printf("none\n");
}
return 0;
}