Description

传送门

Solution

先将原树转化成点分树:

然后维护三个堆:

  • \(c[i]\) 保存点分树中子树 \(i\) 中的黑色节点到 \(fa[i]\) 的距离;
  • \(b[i]\) 保存点分树中 \(i\) 的每个儿子的 \(c[i]\) 的最大值;
  • \(a\) 保存点分治的每个根 \(i\) 的最大答案。

注意重复修改可能会导致 \(b[i]\) 储存了两个在同一子树中的节点,在放入 \(a\) 前需判断。

Code

#include <queue>
#include <cstdio>
#include <algorithm> const int N = 100002;
struct Edge { int v, nxt; } e[N << 1];
struct Pair {
int x, y, z;
bool operator < (const Pair & rhs) const {
return x < rhs.x;
}
};
int head[N], tot, fa[N], st[19][N << 1], rt, son[N], vis[N], col[N], dep[N], cnt, pos[N], siz[N], lg[N << 1];
std::priority_queue<Pair> a, b[N], c[N]; int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
void adde(int u, int v) {
e[++tot].nxt = head[u], head[u] = tot, e[tot].v = v;
}
void dfs(int u, int f) {
dep[u] = dep[f] + 1, st[0][++cnt] = dep[u], pos[u] = cnt;
for (int i = head[u]; i; i = e[i].nxt)
if (e[i].v != f) dfs(e[i].v, u), st[0][++cnt] = dep[u];
}
void getrt(int u, int f) {
siz[u] = 1, son[u] = 0;
for (int i = head[u]; i; i = e[i].nxt) if (e[i].v != f && !vis[e[i].v])
getrt(e[i].v, u), siz[u] += siz[e[i].v], son[u] = std::max(son[u], siz[e[i].v]);
if ((son[u] = std::max(son[u], tot - siz[u])) < son[rt]) rt = u;
}
void solve(int u) {
vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt)
if (!vis[e[i].v]) rt = 0, tot = siz[e[i].v], getrt(e[i].v, u), fa[rt] = u, solve(rt);
}
int get(int l, int r) {
if (l > r) l ^= r, r ^= l, l ^= r;
int k = lg[r - l + 1];
return std::min(st[k][l], st[k][r - (1 << k) + 1]);
}
void insert(int u) {
b[u].push((Pair){0, u, u});
int v = u;
while (fa[v]) c[v].push((Pair){dep[u] + dep[fa[v]] - (get(pos[u], pos[fa[v]]) << 1), u, u}), v = fa[v];
}
void update(int u, int f) {
int v = u;
if (f) b[u].push((Pair){0, u, u});
while (v) {
if (f && fa[v]) c[v].push((Pair){dep[u] + dep[fa[v]] - (get(pos[u], pos[fa[v]]) << 1), u, u});
while (!c[v].empty() && col[c[v].top().y]) c[v].pop();
if (!c[v].empty() && fa[v]) b[fa[v]].push(c[v].top());
while (!b[v].empty() && col[b[v].top().y]) b[v].pop();
if (b[v].empty()) { v = fa[v]; continue; }
Pair x = b[v].top(), y;
b[v].pop();
if (b[v].empty()) { b[v].push(x), v = fa[v]; continue; }
while (!b[v].empty() && (col[(y = b[v].top()).y] || dep[x.y] + dep[y.y] - (get(pos[x.y], pos[y.y]) << 1) < x.x + y.x)) b[v].pop();
if (b[v].empty()) { b[v].push(x), v = fa[v]; continue; }
a.push((Pair){x.x + y.x, x.y, y.y});
b[v].push(x), v = fa[v];
}
}
int main() {
int n = read(); char opt[3];
for (int i = 1, u, v; i < n; ++i) u = read(), v = read(), adde(u, v), adde(v, u);
dfs(1, 0);
for (int i = 2; i <= cnt; ++i) lg[i] = lg[i >> 1] + 1;
for (int i = 1; (1 << i) <= cnt; ++i)
for (int j = 1; j + (1 << i) - 1 <= cnt; ++j)
st[i][j] = std::min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
tot = son[0] = n, getrt(1, 0), solve(rt), tot = n;
for (int i = 1; i <= n; ++i) insert(i);
for (int i = 1; i <= n; ++i) if (!c[i].empty() && fa[i]) b[fa[i]].push(c[i].top());
for (int i = 1; i <= n; ++i) {
Pair x = b[i].top();
b[i].pop();
if (b[i].empty()) { b[i].push(x); continue; }
Pair y = b[i].top();
b[i].push(x), a.push((Pair){x.x + y.x, x.y, y.y});
}
for (int m = read(); m; --m) {
scanf("%s", opt);
if (opt[0] == 'G') {
if (!tot) puts("-1");
else if (tot == 1) puts("0");
else {
while (col[a.top().y] || col[a.top().z]) a.pop();
printf("%d\n", a.top().x);
}
} else {
int x = read();
if (!col[x]) col[x] = 1, --tot, update(x, 0);
else col[x] = 0, ++tot, update(x, 1);
}
}
return 0;
}

[BZOJ 1095] [ZJOI 2007] 捉迷藏的更多相关文章

  1. [BZOJ 1095] [ZJOI 2007]Hide 捉迷藏

    在BZ上连续MLE n次后,终于A了. 自己YY的动态点分写法,思路还是很清楚的,但是比较卡内存. 用到了MAP导致复杂度比其他的代码多了一个log,看来需要去借鉴一下别人怎么写的. updata i ...

  2. BZOJ 1095: [ZJOI2007]Hide 捉迷藏

    Description 一棵树,支持两个操作,修改一个点的颜色,问树上最远的两个白点距离. Sol 动态点分治. 动态点分治就是将每个重心连接起来,形成一个跟线段树类似的结构,当然它不是二叉的... ...

  3. bzoj 1095 [ZJOI2007]Hide 捉迷藏(括号序列+线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1095 [题意] 给定一棵树,树上颜色或白或黑而且可以更改,多个询问求最远黑点之间的距离 ...

  4. 数据结构(括号序列,线段树||点分治,堆):ZJOI 2007 捉迷藏

    [题目描述] Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N- ...

  5. 洛谷.4115.Qtree4/BZOJ.1095.[ZJOI2007]Hide捉迷藏(动态点分治 Heap)

    题目链接 洛谷 SPOJ BZOJ1095(简化版) 将每次Solve的重心root连起来,会形成一个深度为logn的树,就叫它点分树吧.. 我们对每个root维护两个东西: 它管辖的子树中所有白点到 ...

  6. 【刷题】BZOJ 1095 [ZJOI2007]Hide 捉迷藏

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  7. [ZJOI 2007] 捉迷藏

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1095 [算法] 首先建出点分树,然后每一个点开两个堆.“第一个堆记录子树中所有节点到 ...

  8. 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV

    意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...

  9. BZOJ 1095: [ZJOI2007]Hide 捉迷藏(线段树维护括号序列)

    这个嘛= =链剖貌似可行,不过好像代码长度很长,懒得打(其实是自己太弱了QAQ)百度了一下才知道有一种高大上的叫括号序列的东西= = 岛娘真是太厉害了,先丢链接:http://www.shuizilo ...

随机推荐

  1. Webpack 4教程 - 第四部分,使用SplitChunksPlugin分离代码

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/06/04/code-splitting-with-s ...

  2. ubuntu:xxx is not in the sudoers file. 问题解决

    ubuntu 下普通用户用 sudo 执行命令时报 "xxx is not in the sudoers file.This incident will be reported" ...

  3. 操作MongoDB数据库知识点

    一.命令行操作mongo: 1.开启数据库 mongo 如果启动mongo报以下错误: 运行brew services start mongodb 2.创建数据库并进入实例 use test 3.查看 ...

  4. JQ的.serialize()

    前面写的都是用Form表单提交,但是VUE.JS好像不能控制Form的Action. 于是就用AJAX来提交,但是跳转地址(window.location.href=)会暴露数据在url上,就直接用s ...

  5. 简简单单的Vue1(MVVM与Vue的双向绑定原理)

    既然选择了远方,便只顾风雨兼程 __ HANS许 系列:零基础搭建前后端分离项目 系列:零基础搭建前后端分离项目 Vue 在此之前的文章我们讲述了前端开发的工具,语言的知识,接下来我们从头开始学习一个 ...

  6. mysql 存储ip地址

    mysql提供了两个方法来处理ip地址: inet_aton 把ip转为无符号整型(4-8位) inet_ntoa 把整型的ip转为电地址 插入数据前,先用inet_aton把ip地址转为整型,可以节 ...

  7. Vmware安装CentOs7+gitlab(一)

    本篇文章主要介绍了VMware安装Centos7超详细过程(图文),具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.软硬件准备 软件:推荐使用VMwear,我用的是VMwear 12 镜像:Ce ...

  8. axios 封装

    来自:https://www.jianshu.com/p/68d81da4e1ad 侵删 import axios from 'axios' import qs from 'qs' let baseu ...

  9. (最完美)MIUI12系统的Usb调试模式在哪里开启的步骤

    当我们使用安卓手机通过数据线链接到Pc的时候,或者使用的有些app比如我们公司营销小组当使用的app引号精灵,之前的老版本就需要开启usb调试模式下使用,现当新版本不需要了,如果手机没有开启usb调试 ...

  10. SQL SERVER 2012 AlwaysOn - 操作系统层面 01

    搭建 AlwaysOn 是件非常繁琐的工作,需要从两方面考虑,操作系统层面和数据库层面,AlwaysOn 非常依赖于操作系统,域控,群集,节点等概念: DBA 不但要熟悉数据库也要熟悉操作系统的一些概 ...