競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 131F

ABC131F

格子点を2部グラフとして扱う.

点 \(\,(x,y)\,\) が与えられたとき, グラフ \(G\) において 頂点 \(v_{x}, v_{y}\) と 向無辺 \(\,\{v_{x}, v_{y}\}\,\) を作る. 点として現れる \(x\) 座標全体,\(y\) 座標全体を \(a_{x}, a_{y}\) とおく.
\(cx := \{v_{x} \in V_{G} \ \vert \ x \in a_{x} \}\), \(cy := \{v_{y} \in V_{G} \ \vert \ y \in a_{y} \}\) とおく. \(c\) に辺を追加して完全グラフにしたいので, \(\#cx \cdot \#cy = \#E(c) + \) (張られる辺の本数) \( \cdots (*)\) となる. よって, \(G\) の連結成分 \(c\) に対する答え = 張られる辺の本数 = \(\#cx \cdot \#cy - \#E(c)\) となる. ゆえに,\(G\) 全体での答えは \( \sum_{c} (\#cx \cdot \#cy - \#E(c)) = \sum_{c} (\#cx \cdot \#cy) - \#E(G) \) となる.

説明
上の方法で計算できる理由を説明する. 以下の場合分けで,いずれのケースも式 \((*)\) が成り立つ.

  • 長さが 1の path しかないとき. これは \(c\) が既に完全2部グラフ.
  • 長さが 2の path しかないとき. これは \(c\) が既に完全2部グラフ.
  • 長さが 3以上の path が存在するとき. 辺の長さの偶奇に依らず, 辺の追加を帰納的に行うことで, \(p\) を完全2部グラフにできる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n;
  cin >> n;

  map<ll,vll> to;
  const ll C = 1e5 + 5;
  vll vx;
  rep(i,n){
    ll x,y; cin >> x >> y;
    y += C;
    to[x].push_back(y);
    to[y].push_back(x);
    vx.push_back(x);
  }

  map<ll,bool> vis;
  ll cx = 0, cy = 0;
  auto dfs = [&](auto dfs, ll cu, ll pa = -1) -> void {
    if(vis[cu]) return;
    vis[cu] = true;
   
    if(cu < C) cx++;
    else cy++;

    for(auto ne: to[cu]) if(ne != pa){
      dfs(dfs, ne, cu);    
    }
  };

  ll ans = -n;
  rep(i,n) {
    cx = cy = 0;
    dfs(dfs, vx[i]);
    ans += cx * cy;
  }
  cout << ans << endl;

  return 0;
}