CF1477D Nezzar and Hidden Permutations 题解

Description

给定一张 nn 个点 mm 条边的简单无向图,构造两个排列 p,qp,q,使得:

  • 对任意 (u,v)E(u,v)\in E(pupv)(quqv)>0(p_u-p_v)(q_u-q_v)>0.
  • 在此基础上,最大化 {i  piqi}\left|\left\{i\ |\ p_i\neq q_i\right\}\right|.

1n,m5×1051\leq n,m\leq 5\times 10^5

Solution

首先显然如果存在一个点 xx 的度数等于 n1n-1,则将边定向后这个点的拓扑序一定唯一,所以这个位置一定会相同,可以把 px,qxp_x,q_x 赋值为 nn,然后将这个点删掉。

那么剩下的点一定满足度数 n2\leq n-2,可以猜测剩下的点一定没有位置相同。

考虑把现在的图的补图建出来,则一定构成若干个大小不小于 22 的连通块。

如果补图是恰好一个菊花图,设菊花心为 xx,其它点为 a1,a2,,an1a_1,a_2,\ldots,a_{n-1},让 px=1,pai=i+1,qx=n,qai=ip_x=1,p_{a_i}=i+1,q_x=n,q_{a_i}=i 可以满足条件且没有位置相同。

这启发我们对于一般情况去把补图划分成若干个大小不小于 22 的菊花图。

下面给出构造:

  1. 从前往后扫 ii,如果 ii 已经被划分进菊花图了,就不管。
  2. 否则遍历 ii 的邻域,如果存在点 jj 使得 jj 没被划分进菊花图,就让 iiii 邻域所有没被划分的点放到一个菊花图里。
  3. 如果找不到这样的 jj,就随便取 ii 邻域的一个点 kk,如果 kk 所在菊花大小等于 22,则将 kk 所在菊花的菊花心设为 kk,并对 kk22 过程。
  4. 如果菊花大小大于 22,就把 kk 从原来的菊花里踢出,然后和 ii 组成菊花。根据上面的构造方式,一定能保证 kk 不是原来所在菊花的心,所以构造方式仍然合法。

注意补图可能边数很多,这里只需要找到补图的一个生成树,找生成树时就维护一个 set 表示目前没被遍历的点,每次拓展时就暴力遍历 set,找到不与当前点在原图上有边的点,并在补图上连边即可。

时间复杂度:O((n+m)logn)O((n+m)\log n)

Code

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 5e5 + 5;

int n, m, cnt;
int u[kMaxN], v[kMaxN], p[kMaxN], q[kMaxN], deg[kMaxN], bel[kMaxN];
bool del[kMaxN];
std::vector<int> G[kMaxN], T[kMaxN];
std::set<int> chry[kMaxN];

void init() {
cnt = 0;
for (int i = 1; i <= n; ++i)
G[i].clear(), T[i].clear(), chry[i].clear(), p[i] = q[i] = deg[i] = bel[i] = del[i] = 0;
}

void solve1() { // 求出度数为 n - 1 的点
std::set<std::pair<int, int>> st;
for (int i = 1; i <= n; ++i) st.emplace(deg[i] = G[i].size(), i);
int now = n;
for (int i = 1; i <= n; ++i) {
if (prev(st.end())->first != now - 1) break;
int u = prev(st.end())->second;
st.erase(prev(st.end()));
p[u] = q[u] = ++cnt, --now, del[u] = 1;
for (auto v : G[u]) {
if (!del[v]) {
st.erase({deg[v], v});
--deg[v];
st.emplace(deg[v], v);
}
}
}
for (int i = 1; i <= n; ++i) G[i].clear();
for (int i = 1; i <= m; ++i)
if (!del[u[i]] && !del[v[i]])
G[u[i]].emplace_back(v[i]), G[v[i]].emplace_back(u[i]);
}

void solve2() { // 求出补图生成森林
std::set<int> st;
for (int i = 1; i <= n; ++i) {
if (!del[i]) st.emplace(i);
std::sort(G[i].begin(), G[i].end());
}
std::function<void(int)> dfs = [&] (int u) {
std::vector<int> vec;
for (auto v : st)
if (std::find(G[u].begin(), G[u].end(), v) == G[u].end())
vec.emplace_back(v), T[u].emplace_back(v), T[v].emplace_back(u);
for (auto v : vec) st.erase(v);
for (auto v : vec) dfs(v);
};
for (int i = 1; i <= n; ++i) {
if (!del[i] && st.count(i)) {
st.erase(i);
dfs(i);
}
}
}

void solve3() { // 求出菊花划分
for (int i = 1; i <= n; ++i) {
if (!del[i] && !bel[i]) {
bool fl = 0;
for (auto j : T[i]) fl |= !bel[j];
if (fl) {
chry[i].emplace(i), bel[i] = i;
for (auto j : T[i]) {
if (!bel[j]) chry[i].emplace(j), bel[j] = i;
}
} else {
int j = T[i][0];
if (chry[bel[j]].size() == 2) {
chry[j].swap(chry[bel[j]]);
bel[bel[j]] = j;
for (auto x : T[j]) {
if (!bel[x]) chry[j].emplace(x), bel[x] = j;
}
} else {
chry[bel[j]].erase(j);
bel[i] = bel[j] = i;
chry[i].emplace(i), chry[i].emplace(j);
}
}
}
}
int sum = cnt;
for (int i = 1; i <= n; ++i) sum += chry[i].size();
for (int i = 1; i <= n; ++i) {
if (chry[i].size()) {
p[i] = cnt + 1;
int now = 1;
for (auto x : chry[i]) {
if (x != i) p[x] = cnt + (++now);
}
q[i] = cnt + now;
now = 0;
for (auto x : chry[i]) {
if (x != i) q[x] = cnt + (++now);
}
cnt += chry[i].size();
}
}
}

void dickdreamer() {
std::cin >> n >> m;
init();
for (int i = 1; i <= m; ++i) {
std::cin >> u[i] >> v[i];
G[u[i]].emplace_back(v[i]), G[v[i]].emplace_back(u[i]);
}
solve1(), solve2(), solve3();
for (int i = 1; i <= n; ++i) std::cout << p[i] << " \n"[i == n];
for (int i = 1; i <= n; ++i) std::cout << q[i] << " \n"[i == n];
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1477D Nezzar and Hidden Permutations 题解
https://sobaliuziao.github.io/2024/12/20/post/af0930c7.html
作者
Egg_laying_master
发布于
2024年12月20日
许可协议