bzoj 1095 [ZJOI2007]Hide 捉迷藏 动态点分治+堆

题面

题目传送门

解法

挺恶心的题

考虑动态点分治,先建出点分树

然后每一个点开两个堆,分别为\(a,b\)

\(a_i\)表示点分树上\(i\)子树中所有节点在原树上和点分树中\(i\)父亲的距离,\(b_i\)表示点分树中\(i\)所有儿子的堆顶

再开一个堆\(ans\),存每一个\(b_i\)最大和次大值的和

在修改的时候,分两种情况考虑

但是本质都是一样的,就是在点分树上不断爬,爬到根为止,然后对当前点和父亲的\(a,b\)进行删除和加入

细节比较多,需要注意

重点:传入一个结构体参数的时候一定要加&,否则会T成皮皮

时间复杂度:\(O(q\ log^2\ n)\)

代码

#include <bits/stdc++.h>
#define inf 1 << 30
#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 Heap {
    priority_queue <int> ret, del;
    void push(int x) {ret.push(x);}
    void erase(int x) {del.push(x);}
    int siz() {return ret.size() - del.size();}
    void Pop() {
        while (!ret.empty() && !del.empty() && ret.top() == del.top())
            ret.pop(), del.pop();
        if (ret.size()) ret.pop();
    }
    int top() {
        if (!siz()) return 0;
        while (!ret.empty() && !del.empty() && ret.top() == del.top())
            ret.pop(), del.pop();
        return ret.top();
    }
    int setop() {
        if (siz() < 2) return 0;
        int x = top(); Pop();
        int y = top(); push(x);
        return y;
    }
} a[N], b[N], ans;
struct Edge {
    int next, num;
} e[N * 3];
int n, tot, cnt, rt, now, c[N], d[N], f[N], p[N], dep[N], siz[N], vis[N], ff[N][20];
vector <int> E[N];
void add(int x, int y) {
    e[++cnt] = (Edge) {e[x].next, y};
    e[x].next = cnt;
}
void getr(int x, int fa) {
    f[x] = 0, siz[x] = 1;
    for (int i = 0; i < E[x].size(); i++) {
        int k = E[x][i];
        if (k == fa || vis[k]) continue;
        getr(k, x); chkmax(f[x], siz[k]);
        siz[x] += siz[k];
    }
    chkmax(f[x], now - siz[x]);
    if (f[x] < f[rt]) rt = x;
}
void work(int x, int fa) {
    vis[x] = 1, p[x] = fa;
    if (fa) add(fa, x);
    for (int i = 0; i < E[x].size(); i++) {
        int k = E[x][i];
        if (vis[k]) continue;
        f[rt = 0] = inf, now = siz[k];
        getr(k, x); work(rt, x);
    }
}
void dfs(int x, int fa) {
    d[x] = d[fa] + 1;
    for (int i = 1; i <= 18; i++)
        ff[x][i] = ff[ff[x][i - 1]][i - 1];
    for (int i = 0; i < E[x].size(); i++) {
        int k = E[x][i];
        if (k == fa) continue; ff[k][0] = x;
        dfs(k, x);
    }
}
int lca(int x, int y) {
    if (d[x] < d[y]) swap(x, y);
    for (int i = 18; i >= 0; i--)
        if (d[ff[x][i]] >= d[y]) x = ff[x][i];
    if (x == y) return x;
    for (int i = 18; i >= 0; i--)
        if (ff[x][i] != ff[y][i]) x = ff[x][i], y = ff[y][i];
    return ff[x][0];
}
int dis(int x, int y) {
    int z = lca(x, y);
    return d[x] + d[y] - 2 * d[z];
}
void update(int x, int y, int z) {
    a[z].push(dis(x, y));
    for (int p = e[x].next; p; p = e[p].next)
        update(e[p].num, y, z);
}
void getd(int x) {
    dep[x] = dep[p[x]] + 1;
    for (int p = e[x].next; p; p = e[p].next)
        getd(e[p].num);
}
void init() {
    f[rt = 0] = inf;
    dfs(1, 0); now = n;
    getr(1, 0); work(rt, 0);
    for (int i = 1; i <= n; i++)
        if (p[i]) update(i, p[i], i);
    for (int i = 1; i <= n; i++) {
        b[i].push(0);
        for (int p = e[i].next; p; p = e[p].next) {
            int k = e[p].num;
            if (a[k].siz()) b[i].push(a[k].top());
        }
    }
    for (int i = 1; i <= n; i++)
        ans.push(b[i].top() + b[i].setop());
}
void Insert(Heap &a) {
    if (a.siz() >= 2) {
        int t = a.top() + a.setop();
        ans.push(t);
    }
}
void Erase(Heap &a) {
    if (a.siz() >= 2) {
        int t = a.top() + a.setop();
        ans.erase(t);
    }
}
void modify(int x) {
    if (c[x] == 1) {
        Erase(b[x]), b[x].push(0);
        Insert(b[x]);
        for (int y = x; p[y]; y = p[y]) {
            Erase(b[p[y]]);
            if (a[y].siz()) b[p[y]].erase(a[y].top());
            a[y].push(dis(p[y], x));
            if (a[y].siz()) b[p[y]].push(a[y].top());
            Insert(b[p[y]]);
        }
        tot++;
    } else {
        Erase(b[x]); b[x].erase(0);
        Insert(b[x]);
        for (int y = x; p[y]; y = p[y]) {
            Erase(b[p[y]]);
            if (a[y].siz()) b[p[y]].erase(a[y].top());
            a[y].erase(dis(p[y], x));
            if (a[y].siz()) b[p[y]].push(a[y].top());
            Insert(b[p[y]]);
        }
        tot--;
    }
    c[x] ^= 1;
}
int main() {
    read(n); cnt = tot = n;
    for (int i = 1; i < n; i++) {
        int x, y; read(x), read(y);
        E[x].push_back(y), E[y].push_back(x);
    }
    init(); int q; read(q);
    while (q--) {
        char c = getchar();
        while (!isalpha(c)) c = getchar();
        if (c == 'C') {
            int x; read(x);
            modify(x);
        } else
            if (tot < 2) cout << tot - 1 << "\n";
                else cout << ans.top() << "\n";
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/copperoxide/p/9476776.html