AGC006F Blackout

发布时间 2023-07-25 21:41:51作者: MoyouSayuki

AGC006F Blackout

如果一个格子 \((x, y)\) 是黑色的,那么构建边 \(x\rightarrow y\),接下来对于每个弱连通块分类讨论:

  1. 图中有自环 则弱连通块必然形成一个完全图

证明:

从自环开始归纳,将自环视为一个点数为 \(1\) 的完全图,接下来扩展完全图时,分类讨论:

  1. 从完全图中一个点 \(u\),存在边 \(u\rightarrow v\),则根据拓展条件可知,会存在边 \(v\rightarrow u, v\rightarrow v\),对于完全图中除了 \(u\) 任意一点 \(w\),其与 \(u\) 一定有双向的有向边链接则存在边 \(w\rightarrow v\),于是如上所述,一定有 \(v\rightarrow w\),所以 \(v\) 被加入后原图依然是完全图。
  2. 从完全图中一个点 \(u\),存在边 \(v\rightarrow u\),与第一类情况类似,可以证明原图依然是完全图。

得证。

  1. 图中有包含两个点的环 则弱连通块必然形成一个完全图

证明:

如果 \(u\rightarrow v, v\rightarrow u\),则有 \(u\rightarrow u, v\rightarrow v\),则弱连通块中有自环,则弱连通块必然形成一个完全图。

  1. 图中最多只有包含两个点的路径 则弱连通块必然无法拓展,证明显然

  2. 图中只有包含三个及以上个点的路径,进行三染色:

    1. 如果三染色成功

    则图中会分为三个颜色块,定义同一个颜色块里的点颜色相同,颜色块内部无边,颜色块之间互相连边。

    先证明第一个命题:

    证明:

    假设颜色块内部有边,那么三染色会失败,与三染色成功的条件矛盾,则颜色块内部无边,得证。

    接下来证明第二个命题:

    证明:

    如果能成功三染色,那么图中一定存在一个长度为 \(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\) 的情况同理,归纳成立。

    1. 如果三染色失败

    那么原图必然成为一个完全图,分类讨论可以证明。

// 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;
}