1. 程式人生 > >Luogu P4115Qtree4 P2056[ZJOI2007]捉迷藏 題解

Luogu P4115Qtree4 P2056[ZJOI2007]捉迷藏 題解

題目連結

題解 動態點分治+堆

點分樹 : 我們把分治過程中遍歷過的重心都連起來 上一層的重心連線下一層的重心 可以得到一棵新的樹

然後在這顆樹上亂搞

先對於每個點弄兩個大根堆

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