bzoj 1095 [ZJOI2007]Hide 捉迷藏 動態點分治+堆/線段樹
阿新 • • 發佈:2018-12-10
題面
解法
資料結構題……
- 講一下兩種不同的思路吧,用括號序列怎麼做我不會
- 因為有修改並且有關於點之間距離的詢問,所以我們考慮動態點分治
- 首先建出點分樹,然後每一個點開兩個堆。“第一個堆記錄子樹中所有節點到父親節點的距離,第二個堆記錄所有子節點的堆頂,那麼一個節點的堆2中的最大和次大加起來就是子樹中經過這個節點的最長鏈。然後我們最後開一個全域性的堆,記錄所有堆2中最大值和次大值之和。那麼全域性的堆頂就是答案。”—PoPoQQQ(
因為我比較懶,就不打算自己打了) - 時間複雜度:
- 考慮一下另外一種做法,我們可以對於整棵樹的序建出一棵線段樹,每一個點維護這段
- 顯然,這個資訊可以合併,每一次修改就是單點修改某一個值
- 時間複雜度:(rmq求)或
- 這兩種方法均可通過此題
程式碼
因為動態點分治+堆比較麻煩,所以我還是放線段樹的程式碼吧
#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;
}