CF1477D Nezzar and Hidden Permutations(构造)
CF1477D Nezzar and Hidden Permutations(构造)
题目大意
你需要构造出两个排列 p, q,满足 m 个限制,第 i 个限制为 $ (p_{x_i}-p_{y_i})\times (q_{x_i}-q_{y_i}) \ge 0$,最大化 \(\sum [p_i \neq q_i]\)
\(1 \le n,m \le 5\times 10^5\)
解题思路
还是牛逼的猜结论和构造题,但是我哪一步都做不出来 😭
我们把每个限制可以看做是一条边,首先发现如果一个点的度数是 n - 1,必然有 \(p_x=q_x\),我们让 \(p_x=n\),删掉这个点和关于它的限制,做 n - 1 阶的问题,直到最大度数小于 n - 1。
这时候有结论:必定有解使得每个 \(p_i \neq q_i\)!
考虑这样一种基本结构,存在一个点(成为关键点)和其他的所有点都没有边,那么这个点设成 (1, n),其他点设成 (k + 1, k) 可以满足所有的条件,那么如果我们可以把整个图分成若干个集合,每个集合都可以表示为这样的基本结构,那么这个问题就解决了。
幸运的是,这是可以的,考虑两个点 (x, y),其中 x 还没有加入任何集合,满足 x,y 之间没有边。
分类讨论一下:
- 如果 y 没有加入集合,那么将 x,y 新建一个集合;
- 如果 y 在一个大小为 2 的集合中或者 y 是集合中的关键点,我们将 y 设为关键点,把 x 加入集合;
- 否则,我们可以把 y 从集合中删掉,和 x 新建一个集合。
这个构造方式的条件仅是需要满足任何一个点都可以找到另外一个点和它没有边!
这样我们简单模拟即可,注意到是一堆集合操作,可以用 set 轻松维护,看起来我的代码比别人稍短一些。
源代码:https://codeforces.com/contest/1477/submission/107069108(目前次短)
#include <set>
const int N = 505000;
set<int> ed[N], S[N];
int id[N], rt[N], p[N], q[N], T, m, n;
void work(void) {
read(n), read(m);
for (int i = 1;i <= n; ++i) ed[i].clear(), id[i] = 0;
for (int i = 1, x, y;i <= m; ++i)
read(x), read(y), ed[x].insert(y), ed[y].insert(x);
int all = 0, cnt = 0;
for (int i = 1;i <= n; ++i) {
if (ed[i].size() == n - 1) { p[i] = q[i] = ++all; continue; }
if (id[i]) continue;
int to = 1;
ed[i].insert(n + 1);
for (auto y : ed[i]) {
if (i == to) ++to;
if (y != to) break;
++to;
}
auto mk = [&](int x, int y) {
rt[id[x] = id[y] = ++cnt] = x;
S[cnt].insert(x), S[cnt].insert(y);
};
if (!id[to]) { mk(i, to); continue; }
int ak = id[to];
if (S[ak].size() == 2 || rt[ak] == to) S[ak].insert(i), rt[id[i] = ak] = to;
else S[ak].erase(to), mk(i, to);
}
for (int i = 1;i <= cnt; ++i) {
p[rt[i]] = ++all;
for (auto t: S[i])
if (t != rt[i]) q[t] = all, p[t] = ++all;
q[rt[i]] = all, S[i].clear(), rt[i] = 0;
}
for (int i = 1;i <= n; ++i) write(p[i], " \n"[i == n]);
for (int i = 1;i <= n; ++i) write(q[i], " \n"[i == n]);
}