ARC111-B Reversible Cards

これは解けないとダメなやつだった

https://atcoder.jp/contests/arc111/tasks/arc111_b

問題概要

N枚のカードがあり、ある面にはA[i] , その裏面にはB[i]が書かれている。N枚のカードの裏表を自由に選択できる時、表を向いている数の種類は最大で何種類?

  • 1 ≤ N ≤ 200,000
  • 1 ≤ A_i ,B_i ≤ 400,000

解法

  • maxflowの計算量的にフローはダメだろうと思っていたが、どうもうまくやるとできたらしい。とりあえず、典型としてグラフを構成する。

  • A_iとB_iの間に辺を張り、無向グラフを構成する(流石に初見じゃない)

  • こうして構成されたグラフにおいて、カード(A[i] , B[i])のうちどちらかを表にすることは、Edge(A[i] , B[i])において、一方の頂点を色塗ることに対応していて、構成された連結成分が木ならばサイズ-1だけ、木ではないならば全部の頂点を色付けることができる。 類題が思い出せないけどABCとか企業ARCで去年あたり2、3回見た記憶がある...

  • ところで、この方法だとサイズが1の時だけ例外になる。これも含めて計算できる方法もあるのかな

#include<bits/stdc++.h>
using namespace std;

#define LIM 400010

int N;
vector<int> graph[LIM];
int visited[LIM];

int component_size = 0;
bool istree;
void dfs(int x,int pre){
    component_size++;
    visited[x] = 1;
    for(auto e : graph[x]){
        if(e != pre){
            if(visited[e]){
                istree = false;
            }else{
                dfs(e,x);
            }
        }
    }
}

int main(void){
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    cin >> N;
    for (int i = 0; i < N; i++) {
        int a,b;
        cin >> a >> b;
        graph[a].push_back(b);
        graph[b].push_back(a);
    }
    
    int ans = 0;
    for (int i = 0; i < LIM; i++) {
        if(graph[i].size() && !visited[i]){
            component_size = 0;
            istree = true;
            dfs(i , -1);
            if(istree && (component_size != 1)){
                ans += component_size - 1;
            }else{
                ans += component_size;
            }
        }
    }
    
    cout << ans << '\n';
    
}