1. 程式人生 > >bzoj 1095 [ZJOI2007]Hide 捉迷藏 動態點分治+堆/線段樹

bzoj 1095 [ZJOI2007]Hide 捉迷藏 動態點分治+堆/線段樹

題面

題目傳送門

解法

資料結構題……

  • 講一下兩種不同的思路吧,用括號序列怎麼做我不會
  • 因為有修改並且有關於點之間距離的詢問,所以我們考慮動態點分治
  • 首先建出點分樹,然後每一個點開兩個堆。“第一個堆記錄子樹中所有節點到父親節點的距離,第二個堆記錄所有子節點的堆頂,那麼一個節點的堆2中的最大和次大加起來就是子樹中經過這個節點的最長鏈。然後我們最後開一個全域性的堆,記錄所有堆2中最大值和次大值之和。那麼全域性的堆頂就是答案。”—PoPoQQQ(因為我比較懶,就不打算自己打了)
  • 時間複雜度:O(qlog2n)
  • 考慮一下另外一種做法,我們可以對於整棵樹的dfs序建出一棵線段樹,每一個點維護這段
    dfs
    序區間對應的點中直徑的兩個端點
  • 顯然,這個資訊可以合併,每一次修改就是單點修改某一個值
  • 時間複雜度:O(qlogn)(rmq求lca)或O(qlog2n)
  • 這兩種方法均可通過此題

程式碼

因為動態點分治+堆比較麻煩,所以我還是放線段樹的程式碼吧

#include <bits/stdc++.h>
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template
<typename node> void chkmin(node &x, node y) {x = min(x, y);} template <typename node> void read(node &x) { x = 0; int f = 1; char c = getchar(); while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();} while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f; } struct
Edge { int next, num; } e[N * 3]; struct Node { int x, y; } t[N * 4]; int cnt, Time, d[N], col[N], dfn[N], rea[N], f[N][17]; void add(int x, int y) { e[++cnt] = (Edge) {e[x].next, y}; e[x].next = cnt; } void dfs(int x, int fa) { d[x] = d[fa] + 1, dfn[x] = ++Time, rea[Time] = x; for (int i = 1; i <= 16; i++) f[x][i] = f[f[x][i - 1]][i - 1]; for (int p = e[x].next; p; p = e[p].next) { int k = e[p].num; if (k == fa) continue; f[k][0] = x; dfs(k, x); } } int dis(int x, int y) { if (!x) swap(x, y); if (x == 0 || y == 0) return (x == 0) ? -1 : 0; int tx = x, ty = y; if (d[x] < d[y]) swap(x, y); for (int i = 16; ~i; i--) if (d[f[x][i]] >= d[y]) x = f[x][i]; if (x == y) return d[tx] + d[ty] - 2 * d[x]; for (int i = 16; ~i; i--) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; x = f[x][0]; return d[tx] + d[ty] - 2 * d[x]; } void update(int k) { int lc = k << 1, rc = k << 1 | 1, ansx = 0, ansy = 0; int mx = -INT_MAX, tmp[5] = {0, t[lc].x, t[lc].y, t[rc].x, t[rc].y}; for (int i = 1; i < 4; i++) for (int j = i + 1; j <= 4; j++) { int x = tmp[i], y = tmp[j], z = dis(x, y); if (z > mx) mx = z, ansx = x, ansy = y; } t[k] = (Node) {ansx, ansy}; } void build(int k, int l, int r) { if (l == r) {t[k].x = rea[l], col[rea[l]] = 1; return;} int mid = (l + r) >> 1; build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r); update(k); } void modify(int k, int l, int r, int x) { if (l == r) {col[rea[x]] ^= 1, t[k].x = (col[rea[x]] == 1) ? rea[x] : 0; return;} int mid = (l + r) >> 1; if (x <= mid) modify(k << 1, l, mid, x); else modify(k << 1 | 1, mid + 1, r, x); update(k); } int main() { int n; read(n); cnt = n; for (int i = 1; i < n; i++) { int x, y; read(x), read(y); add(x, y), add(y, x); } dfs(1, 0); build(1, 1, n); int q, tot = n; read(q); while (q--) { char c = getchar(); while (!isalpha(c)) c = getchar(); if (c == 'G') { if (tot < 2) {cout << tot - 1 << "\n"; continue;} cout << dis(t[1].x, t[1].y) << "\n"; continue; } int x; read(x); if (col[x]) tot--; else tot++; modify(1, 1, n, dfn[x]); } return 0; }