【图论】tarjan求强连通分量:51nod 1456 小K的技术
51nod 1456 小K的技术#
题目描述##
给n个点,m个点对(ai,bi),最初图上无边,要求连最少的边,使得满足这m个点对间ai到bi有路径相连。规定a到b有路,且b到c有路时,a到c也有路。输出最小连边数。
输入样例
4 5
1 2
1 3
1 4
2 3
2 4
输出样例
3
题解##
不妨将这m对点当作m条边,建图后,当一个连通块内存在强连通分量时,该连通块内所需边数为连通块点数,若不存在强连通分量,则该连通块所需的边数为连通块点数-1.(可画图验证)
代码##
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m;
int low[maxn],dfn[maxn],tot,deep,st[maxn],top,vis[maxn],num[maxn],color[maxn],t[maxn],tag[maxn],mark[maxn];
vector<int> e[maxn];
int b;
int ans;
int fa[maxn];
int _find(int x)
{
if(fa[x]==x) return x;
return fa[x]=_find(fa[x]);
}
void unite(int x,int y)
{
int fax=_find(x),fay=_find(y);
if(fax==fay)return ;
fa[fax]=fay;
}
void tarjan(int u)
{
low[u]=dfn[u]=++deep;
vis[u]=1;
st[++top]=u;
int sz=e[u].size();
for(int i=0;i<sz;i++)
{
int v=e[u][i];
unite(u,v);
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else
{
if(vis[v])
low[u]=min(low[u],low[v]);
}
}
if(low[u]==dfn[u])
{
color[u]=++tot;
vis[u]=0;
while(st[top]!=u)
{
color[st[top]]=tot;
vis[st[top--]]=0;
sz++;
}
top--;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
}
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++) t[color[i]]++;
for(int i=1;i<=n;i++)
{
int fai=_find(i);
if(t[color[i]]>1) tag[fai]++;
num[fai]++;
}
for(int i=1;i<=n;i++)
{
int fai=_find(i);
if(!mark[fai])
{
mark[fai]=1;
if(tag[fai]) ans+=num[fai];
else ans+=num[fai]-1;
}
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号