문제: 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