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

原文地址:https://www.cnblogs.com/copperoxide/p/9476776.html

时间: 2024-08-28 22:40:59

bzoj 1095 [ZJOI2007]Hide 捉迷藏 动态点分治+堆的相关文章

BZOJ 1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆

题目大意:给定一棵树,一开始每个点都是黑点,多次改变某个点的状态或询问距离最远的两个黑点的距离 <珍爱生命远离STL可是我还是可耻地用了STL系列> 传说中的动态树分治...其实并没有那么神嘛= = ↑别听这傻瓜瞎说这货被STL卡了一天QAQ 我们把分治过程中遍历过的重心都连起来 上一层的重心链接下一层的重心 可以得到一棵新的树 下面我们开始讨论这棵新树 显然这棵树的高度不会超过O(logn) 然后我们每个节点开两个堆 第一个堆记录子树中所有节点到父亲节点的距离 第二个堆记录所有子节点的堆顶

【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆

[BZOJ1095][ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激

bzoj 1095: [ZJOI2007]Hide 捉迷藏

Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋 子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的 时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要 求打开某个房间的电灯或者关闭某个房间

bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习

好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状(菊花啊..链啊..扫把啊..)这就导致各种方法都会被卡 于是通过每次找重心保证最大深度 动态怎么解决呢? 不妨考虑线段树是二分的固态版本(只可意会),那么我们把每次找到的重心固定下来长成一棵树就可以把点分治凝固(不可言传) 原来点分治该维护什么现在就维护什么... (事实上我并没有写过静态点分治..

bzoj 1095 Hide 捉迷藏 - 动态点分治 -堆

捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯.为了评估某一次游戏的复杂

【BZOJ1095】【ZJOI2007】捉迷藏 [动态点分治]

捉迷藏 Time Limit: 40 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏. 他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达. 游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯. 在起初

【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏

1095: [ZJOI2007]Hide 捉迷藏 Time Limit: 40 Sec  Memory Limit: 162 MB Submit: 1232  Solved: 501 [Submit][Status] Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子

[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

[bzoj1095][ZJOI2007]Hide 捉迷藏 2015年4月20日7,8876 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在

bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 查询最远点对,带修改 显然可以用动态点分治 对于每个点,维护两个堆 堆q1[x] 维护 点分树x的子树中,所有黑点到x的点分树中父节点的距离 堆q2[x]维护 点分树x的子节点的堆q1的堆顶,即若y是x在点分树中的子节点,则q2[x].push(q1[y].top()) 再来维护一个全局的堆Q,维护所有q2的堆顶,即Q.push(q2[x].top()) #include<cmath> #