只要你跑的够快,锅就追不上你

0%

「学习笔记」弦图与完美消除序列

弦图

弦图是一类特殊的无向图。弦图上的所有简单环上都至少有一条弦,其中弦是联结环上两个不相邻的点的边。

这个限制等价于图中存在至少一个完美消除序列。

(好像学了一个没啥用的东西)

完美消除序列

一个完美消除序列是一个排列 $a_1, a_2, \cdots, a_n$,满足对于任意的 $i$,都有 $a_i, a_{i + 1}, \cdots, a_n$ 形成的导出子图中与 $a_i$ 相邻的点两两之间有边。

如何求完美消除序列呢?共有两种算法,可以在 CDQ 的 PPT 中学习,时间复杂度可以做到 $O(n + m)$。

例题

题目大意

给定一个 $n$ 个点 $m$ 条边的弦图,求它的色数。

数据范围:$n \le 10^4, m \le 10^6$。

思路分析

求出一个完美消除序列,然后贪心地倒着染色。不难发现这肯定是对的,因为与当前点相邻的染过色的点的颜色互不相同。

因为图的色数是 $O(\sqrt m)$ 的,所以总共染色的复杂度是 $O(n \sqrt m)$,可以通过本题。

代码实现

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 2e4;
5
int n, m, a[maxn + 3], c[maxn + 3], l[maxn + 3], r[maxn + 3], col[maxn + 3], vis[maxn + 3];
6
bool b[maxn + 3];
7
vector<int> G[maxn + 3];
8
9
void get_array() {
10
	for (int i = 0; i <= 2 * n; i++) {
11
		l[i] = r[i] = -1;
12
	}
13
	for (int i = 1, t = 0; i <= n; i++) {
14
		l[n + i] = t, r[t] = n + i, t = n + i;
15
	}
16
	int mx = 0;
17
	for (int k = 1; k <= n; k++) {
18
		while (r[mx] == -1) {
19
			mx--;
20
		}
21
		int u = r[mx] - n, t = r[u + n];
22
		l[t] = mx, r[mx] = t;
23
		a[k] = u, b[u] = true;
24
		for (int i = 0, v, lx, rx; i < G[u].size(); i++) {
25
			v = G[u][i];
26
			if (b[v]) {
27
				continue;
28
			}
29
			lx = l[n + v], rx = r[n + v];
30
			r[lx] = rx, l[rx] = lx, c[v]++;
31
			l[n + v] = r[n + v] = -1;
32
			if (~r[c[v]]) {
33
				l[r[c[v]]] = n + v, r[n + v] = r[c[v]];
34
			}
35
			r[c[v]] = n + v, l[n + v] = c[v];
36
		}
37
		mx++;
38
	}
39
}
40
41
int main() {
42
	scanf("%d %d", &n, &m);
43
	for (int i = 1, u, v; i <= m; i++) {
44
		scanf("%d %d", &u, &v);
45
		G[u].push_back(v), G[v].push_back(u);
46
	}
47
	get_array();
48
	int ans = 0;
49
	for (int _ = 1, i; _ <= n; _++) {
50
		i = a[_];
51
		for (int j = 0; j < G[i].size(); j++) {
52
			vis[col[G[i][j]]] = i;
53
		}
54
		for (int j = 1; j <= n; j++) {
55
			ans = max(ans, j);
56
			if (vis[j] != i) {
57
				col[i] = j;
58
				break;
59
			}
60
		}
61
	}
62
	printf("%d\n", ans);
63
	return 0;
64
}