【图论】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;
}
posted @ 2019-10-28 17:47  JWizard  阅读(111)  评论(0)    收藏  举报