P8119 「Wdoi-1.5」幻想鄉遊覽計劃

CJzdc發表於2024-11-16

不妨考慮樹的情況,對於圖只要取一棵生成樹即可。

\(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;
}

相關文章