「刷题笔记」Tarjan

贴一个讲得非常详细的\(tarjan\)入门教程

信息传递

讲个笑话:我之前用并查集求最小环过的这题,然后看见题目上有个\(tarjan\)标签
留下了深刻的印象:\(tarjan\)就是并查集求最小环
丢死人了
那么这题题意也很明确了,就是求一个最小环,并查集啥的就不想他了,考虑一下\(tarjan\)的做法
这道题里,就是我们求出每个强连通分量,然后看每个强连通分量最小大小是多少就好
贴一下板子qwq

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define ZZ_zuozhe int main()
#define E 200050

struct edge
{
	ll u,v;
}e[E];
ll tot,head[E],next[E];
void add(ll a,ll b)
{
	++tot;
	e[tot].u=a;
	e[tot].v=b;
	next[tot]=head[a];
	head[a]=tot;
}

ll n,t;

ll dfs[E],low[E],vis[E],cnt=0;
stack<ll> S;
ll pcn=0,pp[E];
void tarjan(ll u)
{
	dfs[u]=low[u]=++cnt;
	S.push(u);
	vis[u]=1;
	for(int i=head[u];i;i=next[i])
	{
		ll v=e[i].v;
		if(!dfs[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])low[u]=min(low[u],low[v]);
	}
	if(dfs[u]==low[u])
	{
		++pcn;
		ll tmp;
		do{tmp=S.top();S.pop();pp[pcn]++;vis[tmp]=0;}while(tmp!=u);
	}
}

ZZ_zuozhe
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&t);
		add(i,t);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfs[i])tarjan(i);
	}
	ll ans=E+1;
	for(int i=1;i<=pcn;i++)
	{
		//cout<<pp[i]<<endl;
		if(pp[i]>1)ans=min(ans,pp[i]);
	}
	printf("%lld",ans);
	return 0;
}

受欢迎的牛

由题意,我们首先可以得出,所有满足题意的牛都是在同一个强连通分量里的,但是给出的图中有许多强连通分量,哪些才是满足要求的呢?
首先,会不会有两个及以上满足要求的强连通分量呢?显然不行,因为那样就不符合强连通分量的定义了:

有向图的极大强连通子图,称为强连通分量

所以,我们只需要找出一个满足要求的强连通分量。
考虑要满足的要求,那么所有强连通分量都是有一条路通向我们所求的这个强连通分量的,再考虑前面强连通分量的定义,就会发现我们所求的这个强联通分量出度必须为\(0\)
与此同时,如果出现了多个出度为\(0\)的强连通分量,那么这两个强连通分量肯定是无法与彼此连通的,这种情况下,答案为\(0\)
同时,如果只有一个出度为\(0\)的强连通分量,那么答案即为这个强连通分量中包含点的个数。
码很好打,这里只是放一下,但这题思路着实挺有意思的awa

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define ZZ_zuozhe int main()
#define N 10005
#define E 500005


struct edge
{
	ll u,v;
}e[E];
ll tot=0,head[E],next[E];
void add(ll a,ll b)
{
	++tot;
	e[tot].u=a;
	e[tot].v=b;
	next[tot]=head[a];
	head[a]=tot;
}

ll n,m,a,b;

ll dfs[E],low[E],vis[E],cnt=0;
ll pcn=0,pp[E],col[E],out[E];
stack<ll>S;

void tarjan(ll u)
{
	dfs[u]=low[u]=++cnt;
	vis[u]=1;
	S.push(u);
	for(int i=head[u];i;i=next[i])
	{
		ll v=e[i].v;
		if(!dfs[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])low[u]=min(low[u],dfs[v]);
	}
	if(low[u]==dfs[u])
	{
		++pcn;
		ll tmp;
		do{tmp=S.top();S.pop();vis[tmp]=0;pp[pcn]++;col[tmp]=pcn;}while(tmp!=u);
	}
}

ZZ_zuozhe
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a,&b);
		add(a,b);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfs[i])tarjan(i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j;j=next[j])
		{
			if(col[i]!=col[e[j].v])out[col[i]]++;
		}
	}
	bool flag=1;
	ll ans=0;
	for(int i=1;i<=pcn;i++)
	{
		if(out[i]==0)
		{
			if(flag)
			{
				ans=pp[i];
				flag=0;
			}
			else
			{
				printf("0\n");
				return 0;
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}

拓展:
一个现在还并没有看懂的证明qaq
(感觉这个证明多加了个无环的条件,反正就先把所有\(DAG\)换成有向图理解吧qaq

猜你喜欢

转载自www.cnblogs.com/zzzuozhe-gjy/p/13384702.html