Codeforces 117C Cycle 题解 [ 蓝 ] [ 竞赛图 ] [ Tarjan ] [ 构造 ]
困难图论题。
结论 \(1\):如果一张竞赛图存在环,那么一定存在三元环。
证明:
取环上连续的三个点 \(a\to b\to c\),根据竞赛图的性质,\(a, c\) 之间必然存在一条边:
- 若 \(a\to c\),那么我们可以把 \(a\to b \to c\) 直接替换为 \(a \to c\),使得环的长度减小 \(1\)。
- 否则满足 \(a\leftarrow c\),那么说明我们直接找到了三元环。
原命题得证。
于是直接使用 Tarjan 找到一个环,模拟上述过程即可。时间复杂度 \(O(n^2)\)。
除此之外,还有一种写起来更简单做法:
结论 \(2\):若有一个子图满足 \(a\to b, a \to c, b \to c\) 的结构,那么我们一定能把 \(a\to c\) 这条边删去使得三元环存在性不变。
证明:
只考虑经过删去边的环 \(x\to a\to c\to x\)。
根据三元环的性质,\(x, b\) 之间必然存在一条边:
- 若 \(b\to x\),那么 \(x\to a \to b\to x\) 的一个环一定存在。
- 若 \(x\to b\),那么 \(x\to b \to c\to x\) 的一个环一定存在。
原命题得证。
因此在执行删边后,最后所有点都只有一个出边,我们只需要用这 \(O(n)\) 条出边判断三元环即可。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 5005;
int n, to[N];
bitset<N> g[N];
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
char c;
cin >> c;
g[i][j] = c - '0';
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
if(g[i][j] && (!to[i] || g[j][to[i]]))
to[i] = j;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(g[j][i] && g[to[i]][j])
{
cout << i << " " << to[i] << " " << j;
return 0;
}
}
}
cout << "-1";
return 0;
}

浙公网安备 33010602011771号