欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF1477D Nezzar and Hidden Permutations 题解

Description

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

  • 对任意 (u,v)E(pupv)(quqv)>0.
  • 在此基础上,最大化 |{i | piqi}|.

1n,m5×105

Solution

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

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

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

如果补图是恰好一个菊花图,设菊花心为 x,其它点为 a1,a2,,an1,让 px=1,pai=i+1,qx=n,qai=i 可以满足条件且没有位置相同。

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

下面给出构造:

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

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

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

Code

#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;
}
posted @   下蛋爷  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起