문제: BOJ 16041 - Double Clique
아이디어 요약
- Double Clique란 
S가 G에서 클릭이고, V−S가 G'(보수 그래프)에서 클릭인 부분집합입니다. 이는 원래 그래프 G에서 S는 클릭, T=V−S는 독립 집합이 되는 스플릿(split) 그래프 분할을 의미합니다. - 정점 차수를 내림차순으로 정렬하고 상위 
s개의 차수 합을 sum_S라 하면, 스플릿 분할의 필요충분식은 sum_S − s(s−1) = 2m − sum_S 입니다.- 좌변은 
S와 T 사이 간선 수 X, 우변은 전체 차수합 2m에서 S의 차수합을 뺀 sum_T = X와 동일해야 합니다. 
 - 위 등식이 처음 성립하는 
s를 찾아 분할 (S,T)를 결정합니다. 이후 경우의 수는 다음 세 가지를 모두 더합니다.- 기본 집합 
S 자체. S 내부에서 차수가 정확히 s−1인 정점 x를 제거해도 T에 간선이 없어 S\\{x}가 유효.T에서 차수가 정확히 s인 정점 y를 추가하여 S∪{y}가 유효. 또한 이런 y와 S 내부에서 차수 s인 정점 x를 교환해도 유효(S∪{y}\\{x}).
 - 총합을 
1e9+7로 모듈러 하여 출력합니다. 인접 리스트는 필요 없고 차수만 있으면 됩니다. 
C++ 풀이
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  | // 42jerrykim.github.io에서 더 많은 정보를 확인 할 수 있습니다.
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    if (!(cin >> n >> m)) return 0;
    vector<int> deg(n, 0);
    for (int i = 0; i < m; ++i) {
        int a, b; cin >> a >> b;
        --a; --b;
        deg[a]++; deg[b]++;
    }
    vector<pair<int,int>> v(n);
    for (int i = 0; i < n; ++i) v[i] = {deg[i], i};
    sort(v.begin(), v.end(), [](const auto& x, const auto& y){
        if (x.first != y.first) return x.first > y.first;
        return x.second < y.second;
    });
    const long long MOD = 1000000007LL;
    long long tot2 = 2LL * m;
    long long sum = 0;
    vector<long long> freq(n + 1, 0); // freq[d]: # of vertices in S with degree d
    int size = -1;
    for (int i = 0; i < n; ++i) {
        long long s = i + 1;
        if (tot2 - s * (s - 1) < 0) break; // early impossibility
        sum += v[i].first;
        int d = v[i].first;
        if (d <= n) freq[d]++;
        long long rest = sum - s * (s - 1); // edges between S and T
        if (rest == tot2 - sum) {          // T has no internal edges
            size = (int)s;
            break;
        }
    }
    if (size == -1) {
        cout << 0 << '\n';
        return 0;
    }
    long long res = 1; // base set S
    for (int i = 0; i < size; ++i) {
        if (deg[v[i].second] == size - 1) {
            res++;
            if (res >= MOD) res -= MOD;
        }
    }
    for (int i = size; i < n; ++i) {
        if (deg[v[i].second] == size) {
            res++; if (res >= MOD) res -= MOD;     // add this vertex to S
            res = (res + freq[size]) % MOD;        // swap with any vertex in S of degree == size
        }
    }
    cout << (res % MOD) << '\n';
    return 0;
}
  | 
복잡도
- 정렬 
O(n log n), 입력 처리 O(m) → 전체 O(n log n + m) 시간, O(n) 추가 메모리. 
참고
- 문제: 
https://www.acmicpc.net/problem/16041 - 콘테스트: NAIPC 2018 B - Double Clique