AGC006F Blackout 题解
AGC006F Blackout
如果一个格子 \((x, y)\) 是黑色的,那么构建边 \(x\rightarrow y\),接下来对于每个弱连通块分类讨论:
- 图中有自环 则弱连通块必然形成一个完全图
证明:
从自环开始归纳,将自环视为一个点数为 \(1\) 的完全图,接下来扩展完全图时,分类讨论:
- 从完全图中一个点 \(u\),存在边 \(u\rightarrow v\),则根据拓展条件可知,会存在边 \(v\rightarrow u, v\rightarrow v\),对于完全图中除了 \(u\) 任意一点 \(w\),其与 \(u\) 一定有双向的有向边链接则存在边 \(w\rightarrow v\),于是如上所述,一定有 \(v\rightarrow w\),所以 \(v\) 被加入后原图依然是完全图。
- 从完全图中一个点 \(u\),存在边 \(v\rightarrow u\),与第一类情况类似,可以证明原图依然是完全图。
得证。
- 图中有包含两个点的环 则弱连通块必然形成一个完全图
证明:
如果 \(u\rightarrow v, v\rightarrow u\),则有 \(u\rightarrow u, v\rightarrow v\),则弱连通块中有自环,则弱连通块必然形成一个完全图。
-
图中最多只有包含两个点的路径 则弱连通块必然无法拓展,证明显然
-
图中只有包含三个及以上个点的路径,进行三染色:
- 如果三染色成功
则图中会分为三个颜色块,定义同一个颜色块里的点颜色相同,颜色块内部无边,颜色块之间互相连边。
先证明第一个命题:
证明:
假设颜色块内部有边,那么三染色会失败,与三染色成功的条件矛盾,则颜色块内部无边,得证。
接下来证明第二个命题:
证明:
如果能成功三染色,那么图中一定存在一个长度为 \(3\) 的链,假设是 \(u\rightarrow v\rightarrow w\),那么一定有 \(w\rightarrow u\),考虑归纳法:
如果当前新加入一个边 \(u\rightarrow v'\),(\(v, w\) 的情况对称,不作讨论 ),因为三染色成功,所以 \(v'\) 的颜色和 \(v\) 相同,属于同一个颜色块,由 \(w\rightarrow u, u\rightarrow v'\) ,一定有 \(v'\rightarrow w\),依然符合原命题,\(v'\rightarrow u\) 的情况同理,归纳成立。
- 如果三染色失败
那么原图必然成为一个完全图,分类讨论可以证明。
// Problem: [AGC006F] Blackout
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-07-22 14:58:28
#include <algorithm>
#include <cstring>
#include <iostream>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, m;
int col[N], cnt[3], ecnt;
vector<int> g[N], ig[N];
bool flg = 0;
void dfs(int u, int c) // 三染色
{
if(~col[u]) return (void)(flg |= (col[u] != c));
col[u] = c;
cnt[c] ++;
for(auto v : g[u])
dfs(v, (c + 1) % 3), ecnt += (u < v);
for(auto v : ig[u])
dfs(v, (c + 2) % 3), ecnt += (u < v);
}
bool st[N];
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(col, -1, sizeof col);
for(int i = 1 ,a, b; i <= m; i ++)
cin >> a >> b, g[a].push_back(b), ig[b].push_back(a);
int ans = 0;
for(int i = 1; i <= n; i ++)
{
if(~col[i]) continue;
cnt[0] = cnt[1] = cnt[2] = 0, flg = 0, ecnt = 0;
dfs(i, 0);
if(flg) ans += (cnt[0] + cnt[1] + cnt[2]) * (cnt[0] + cnt[1] + cnt[2]); // 三染色失败,有自环,则必为完全图
else if(cnt[0] && cnt[1] && cnt[2]) ans += cnt[0] * cnt[1] + cnt[1] * cnt[2] + cnt[0] * cnt[2]; // 三染色成功,三种颜色之间各有一条边
else ans += ecnt; // 染色未果,没有可拓展边
}
cout << ans << '\n';
return 0;
}