[Luogu] CF117C Cycle

\(Link\)

Description

给出一个竞赛图(每两个点之间都有一条有向边),试找出图中的一个三元环,若不存在输出\(-1\)\((n\le5000)\)

Solution

注意到竞赛图如果有一个大环,那么中间一定会有三元环。这是比较好证明的。(下面是蒯的)

假设一个竞赛图存在一个\(N\)元环(大于三元),环上有连续三点\(A , B , C\)( 存在有向边\(AB , BC\)

根据竞赛图的定义,一定存在有向边\(CA\)\(AC\)中的一者。

情况\(1\):若存在\(CA\),则\(A , B , C\)构成三元环;

情况\(2\):若存在\(AC\) ,不考虑\(B\)点,剩下的点构成一个\((N-1)\)元环。显然,如果一直不存在情况\(1\)的话,最终也会形成一个三元环。

这个在纸上自己模拟一下就好了。

所以我们用\(Tarjan\)找出图中每一个强连通分量。注意到对于中间的某个点对\((A,B)\),假设它们在原图上的边是\(A\rightarrow{B}\),又因为\(B\)也可以到\(A\),所以\(A\)\(B\)一定在同一个大环内。所以我们枚举其中每个点对\((A,B)\),再钦定某个强连通分量内的点\(x\),因为\(A\)\(B\)一定在同一个大环内,模拟找三元环的过程,就一定可以找出一个三元环\((A,B,x)\)

Code

#include <bits/stdc++.h>

using namespace std;

int n, t, top, ind, low[5005], dfn[5005], que[5005], vis[5005], rec[5005];

char ch[5005][5005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
	return x * fl;
}

void Tarjan(int x)
{
	dfn[x] = low[x] = ++ ind;
	vis[x] = 1;
	que[ ++ top] = x;
	for (int y = 1; y <= n; y ++ )
	{
		if (ch[x][y] != '1') continue;
		if (!dfn[y])
		{
			Tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (vis[y]) low[x] = min(low[x], dfn[y]);
	}
	if (low[x] == dfn[x])
	{
		t = 0;
		int now = -1;
		do
		{
			now = que[top -- ];
			vis[now] = 0;
			rec[ ++ t] = now;
		} while (now != x);
		for (int i = 1; i <= t - 1; i ++ )
		{
			for (int j = 1; j <= t - 1; j ++ )
			{
				if (ch[x][rec[i]] == '1' && ch[rec[i]][rec[j]] == '1' && ch[rec[j]][x] == '1' && i != j)
				{
					printf("%d %d %d\n", x, rec[i], rec[j]);
				    exit(0);
				}
			}
		}
	}
	return;
}

int main()
{
	n = read();
	for (int i = 1; i <= n; i ++ )
		scanf("%s", ch[i] + 1);
	for (int i = 1; i <= n; i ++ )
		if (!dfn[i])
			Tarjan(i);
	puts("-1");
	return 0;
}
posted @ 2020-11-21 09:02  andysj  阅读(88)  评论(0编辑  收藏  举报