P8119 「Wdoi-1.5」幻想乡游览计划

不妨考虑树的情况,对于图只要取一棵生成树即可。

\(k\le 4n\) 是容易的,两个点分别 dfs 就是 \(\le 4n\) 次。

对于 \(k\le 3n\),考虑一个做法:一个人去遍历整棵树,每次拓展新点时交换。不难证明正确性,次数 \(\le 3n\)

考虑优化这个策略。注意到其中一个点一直在根,这非常浪费。事实上我们可以两个点同时递归两棵子树,每次同时走到一个新点然后交换。

但是这个优化在两棵子树大小差距过大时用处不大,考虑取重心,每个子树划分给某个人去遍历。假设分给两人的大小之和分别为 \(S_1\)\(S_2\),那么总次数就是 \(2\times(S_1+S_2)+\max(S_1,S_2)\)

考虑从大往小考虑每棵子树,每次划分给较小的 \(S\),可以证明 \(\max S\le \dfrac{2}{3}n\),总次数 \(\le\dfrac{8}{3} n\)

code
#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
using namespace std;
void file() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
}
using ll = long long;

const int kL = 5e5 + 5;
int n, m;
array<int, kL> siz;
array<bool, kL> vis;
array<vector<int>, kL> g;
string A = "Ran", B = "Chen";

struct DSU {
  array<int, kL> fa;
  DSU() { iota(ALL(fa), 0); }
  int find(int x) {
    return (fa[x] == x) ? x : (fa[x] = find(fa[x]));
  }
  void merge(int x, int y) { fa[find(x)] = find(y); }
}dsu;

int findrt(int x, int Fa) {
  int mx = 0, rt = 0; siz[x] = 1;
  for(int to : g[x]) {
    if(to == Fa) continue;
    if(int tmp = findrt(to, x))
      rt = tmp;
    siz[x] += siz[to];
    mx = max(mx, siz[to]);
  }
  mx = max(mx, n - siz[x]);
  return (mx * 2 <= n) ? x : rt;
}

void dfs(int x, int Fa) {
  if(Fa) g[x].erase(find(ALL(g[x]), Fa));
  for(int to : g[x]) dfs(to, x);
}

vector<pair<string, int>> ans;

void push(int x, vector<int>& buc) {
  buc.push_back(x);
  for(int to : g[x]) {
    push(to, buc);
    buc.push_back(x);
  }
}

vector<int> vx, vy;

void solve(int x, int Fa) {
  if(g[x].empty()) return ;
  sort(ALL(g[x]), [&](int a, int b) { return siz[a] > siz[b]; });
  for(int to : g[x]) {
    vector<int> buc;
    push(to, buc);
    buc.push_back(x);
    if(vx.size() < vy.size()) {
      for(int i : buc) vx.push_back(i);
    }else {
      for(int i : buc) vy.push_back(i);
    }
  }
}

void getans() {
  int i, j;
  for(i = j = 0; (i < vx.size()) && (j < vy.size()); ) {
    int a = vx[i], b = vy[j];
    if(vis[a]) { ans.emplace_back(A, a); i++; }
    if(vis[b]) { ans.emplace_back(B, b); j++; }
    if(!vis[a] && !vis[b]) {
      i++, j++, vis[a] = vis[b] = 1;
      ans.emplace_back(A, a);
      ans.emplace_back(B, b);
      ans.emplace_back("Swap", -1), swap(A, B);
    }
  }
  for(; i < vx.size(); i++) {
    int p = vx[i];
    if(vis[p]) ans.emplace_back(A, p);
    else {
      vis[p] = 1;
      ans.emplace_back(A, p);
      ans.emplace_back("Swap", -1), swap(A, B);
    }
  }
  for(; j < vy.size(); j++) {
    int p = vy[j];
    if(vis[p]) ans.emplace_back(B, p);
    else {
      vis[p] = 1;
      ans.emplace_back(B, p);
      ans.emplace_back("Swap", -1), swap(A, B);
    }
  }
}

int main() {
  // file();
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for(int i = 1, u, v; i <= m; i++) {
    cin >> u >> v;
    if(dsu.find(u) != dsu.find(v)) {
      dsu.merge(u, v);
      g[u].push_back(v);
      g[v].push_back(u);
    }
  }
  ans.reserve(3 * n);
  vx.reserve(2 * n);
  vy.reserve(2 * n);
  int root = findrt(1, 0);
  assert(findrt(root, 0) == root);
  dfs(root, 0), solve(root, 0), getans();
  cout << root << " ";
  cout << ans.size() << "\n";
  for(auto k : ans) {
    if(~k.second) cout << k.first << " " << k.second << "\n";
    else cout << k.first << "\n";
  }
  return 0;
}
posted @ 2024-11-16 22:08  CJzdc  阅读(5)  评论(0编辑  收藏  举报