CF1477D Nezzar and Hidden Permutations 题解
Description
给定一张
- 对任意
, . - 在此基础上,最大化
.
Solution
首先显然如果存在一个点
那么剩下的点一定满足度数
考虑把现在的图的补图建出来,则一定构成若干个大小不小于
如果补图是恰好一个菊花图,设菊花心为
这启发我们对于一般情况去把补图划分成若干个大小不小于
下面给出构造:
- 从前往后扫
,如果 已经被划分进菊花图了,就不管。 - 否则遍历
的邻域,如果存在点 使得 没被划分进菊花图,就让 和 邻域所有没被划分的点放到一个菊花图里。 - 如果找不到这样的
,就随便取 邻域的一个点 ,如果 所在菊花大小等于 ,则将 所在菊花的菊花心设为 ,并对 做 过程。 - 如果菊花大小大于
,就把 从原来的菊花里踢出,然后和 组成菊花。根据上面的构造方式,一定能保证 不是原来所在菊花的心,所以构造方式仍然合法。
注意补图可能边数很多,这里只需要找到补图的一个生成树,找生成树时就维护一个 set 表示目前没被遍历的点,每次拓展时就暴力遍历 set,找到不与当前点在原图上有边的点,并在补图上连边即可。
时间复杂度:
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步