Luogu P4115Qtree4 P2056[ZJOI2007]捉迷藏 題解
阿新 • • 發佈:2018-12-14
題目連結
題解 動態點分治+堆
點分樹 : 我們把分治過程中遍歷過的重心都連起來 上一層的重心連線下一層的重心 可以得到一棵新的樹
然後在這顆樹上亂搞
先對於每個點弄兩個大根堆
q1[x] 存x的所有白點後代到x的父親 的原樹上的距離 q2[x] 存x的每個子樹中到x 的原樹上的最大距離
顯然, q2為x所有兒子q1的根節點
然後再弄一個大根堆存答案 顯然為每個節點q2的 最大值+次大值
考慮每次更新一個點, 我們只需要從這個點不斷向上更新(在點分樹上)
刪除 : 再記一個標記堆, 操作時對比下當前兩個堆的堆頂是否相等 新增就沒什麼好說的
程式碼複雜,先坑一個50分暴力點分治
#include<bits/stdc++.h> using namespace std; inline int gi() { int f = 1, s = 0; char c = getchar(); while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') f = -1, c = getchar(); while (c >= '0' && c <= '9') s = s*10+c-'0', c = getchar(); return f == 1 ? s : -s; } const int N = 100010; struct node { int to, next; }g[N<<1]; int last[N], gl; void add(int x, int y) { g[++gl] = (node) {y, last[x]}; last[x] = gl; g[++gl] = (node) {x, last[y]}; last[y] = gl; return ; } bool c[N]; int sum, f[N], siz[N], rt; bool vis[N]; void getroot(int u, int fa) { siz[u] = 1; f[u] = 0; for (int i = last[u]; i; i = g[i].next) { int v = g[i].to; if (v == fa || vis[v]) continue; getroot(v, u); siz[u] += siz[v]; f[u] = max(siz[v], f[u]); } f[u] = max(f[u], sum-siz[u]); if (f[u] < f[rt]) rt = u; return ; } int ans, cnt; int dfs(int u, int d, int fa) { int ans = -1000000000; if (!c[u]) ans = d; for (int i = last[u]; i; i = g[i].next) { int v = g[i].to; if (v == fa) continue; ans = max(ans, dfs(v, d+1, u)); } return ans; } int MAX[N]; void solve(int u) { vis[u] = 1; if (!c[u]) MAX[u] = 0, ans = max(ans, 0); else MAX[u] = -1000000000; for (int i = last[u]; i; i = g[i].next) { int v = g[i].to; if (vis[v]) continue; int s = dfs(v, 1, u); ans = max(ans, MAX[u]+s); MAX[u] = max(MAX[u], s); rt = 0; sum = siz[v]; getroot(v, 0); solve(v); } return ; } int main() { int n = gi(); for (int i = 1; i < n; i++) add(gi(), gi()); int m = gi(); while (m--) { char s; cin>>s; if (s == 'C') c[gi()] ^= 1; else { memset(vis, 0, sizeof(vis)); sum = f[0] = n; rt = 0; ans = -1; getroot(1, 0); solve(rt); printf("%d\n", ans); } } return 0; }