题目链接

BZOJ1095

题解

传说中的动态点分治,一直不敢碰

今日一会,感觉其实并不艰涩难懂

考虑没有修改,如果不用树形dp的话,就得点分治

对于每个重心,我们会考虑其分治的子树内所有点到它的距离,然后取所有不同子树中最大的两个相加来更新答案

如果带修改怎么办呢?

考虑一个点\(u\)被修改了,会对哪些点产生影响。显然是包含它的分治子树之中

点分树##

我们在点分治过程中,将每个重心与上一层分治重心相连,形成一棵树结构,叫做点分树

一个点被修改,仅影响其在点分树中所有祖先的答案

那么这样我们就可以维护了

对于每个点\(u\),我们开两个堆分别储存一下信息:

堆\(C\):以\(C\)为根的分治子树中所有点到点分树中\(u\)的父亲的距离

堆\(B\):\(u\)在点分树中所有儿子\(C\)堆堆顶

对于全局开一个堆\(A\),储存所有堆\(B\)的最大值和次大值之和

那么堆\(A\)堆顶就是当前的答案

对于堆来说,插入是很方便的,但是删除就没那么简单,我们可以对每个堆再开一个堆,储存所有被删除的值,每次访问堆顶时,如果删除堆堆顶与当前堆顶相同,那么都\(pop\)掉,直到不相同位置

还要注意的就是堆间操作的顺序

我们要操作\(C\),就得先将父亲\(B\)堆中该\(C\)堆堆顶\(pop\)掉,操作完再加回去

同样,我们要操作\(B\),也得先从\(A\)中将对应贡献删除

感觉这题难点并不在动态点分治。。。而是在堆的维护吧。。。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 100005,maxm = 200005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
struct heap{
priority_queue<int> a,b;
void ins(int x){if (x >= 0) a.push(x);}
void del(int x){if (x >= 0) b.push(x);}
int size(){return a.size() - b.size();}
int top(){
if (!size()) return -1;
while (!b.empty() && a.top() == b.top()) a.pop(),b.pop();
return a.top();
}
int sum(){
if (size() < 2) return -1;
int x = top(); a.pop();
int y = top(); ins(x);
return x + y;
}
}C[maxn],B[maxn],A;
int bin[50],Log[maxm],mn[maxm][19];
int n,fa[maxn],dep[maxn],dfn[maxn],cnt;
int h[maxn],ne = 1;
struct EDGE{int to,nxt;}ed[maxn << 1];
void build(int u,int v){
ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
void dfs(int u){
mn[++cnt][0] = dep[u]; dfn[u] = cnt;
Redge(u) if ((to = ed[k].to) != fa[u]){
fa[to] = u; dep[to] = dep[u] + 1;
dfs(to);
mn[++cnt][0] = dep[u];
}
}
int dis(int u,int v){
int l = dfn[u],r = dfn[v];
if (l > r) swap(l,r);
int t = Log[r - l + 1];
int d = min(mn[l][t],mn[r - bin[t] + 1][t]);
return dep[u] + dep[v] - (d << 1);
}
int F[maxn],Siz[maxn],vis[maxn],Fa[maxn],sum,rt;
int pre[maxn];
void getrt(int u){
F[u] = 0; Siz[u] = 1;
Redge(u) if (!vis[to = ed[k].to] && to != Fa[u]){
Fa[to] = u; getrt(to);
Siz[u] += Siz[to];
F[u] = max(F[u],Siz[to]);
}
F[u] = max(F[u],sum - Siz[u]);
if (F[u] < F[rt]) rt = u;
}
int c[maxn],ci;
void dfs1(int u){
Siz[u] = 1; c[++ci] = u;
Redge(u) if (!vis[to = ed[k].to] && to != Fa[u]){
Fa[to] = u; dfs1(to);
Siz[u] += Siz[to];
}
}
void solve(int u){
vis[u] = true; Siz[u] = 1; ci = 0;
Redge(u) if (!vis[to = ed[k].to]){
Fa[to] = u; dfs1(to);
Siz[u] += Siz[to];
}
if (pre[u]){
C[u].ins(dis(u,pre[u]));
REP(i,ci) C[u].ins(dis(c[i],pre[u]));
}
Redge(u) if (!vis[to = ed[k].to]){
F[rt = 0] = INF; sum = Siz[to];
getrt(to);
pre[rt] = u; to = rt;
solve(rt);
B[u].ins(C[to].top());
}
B[u].ins(0);
A.ins(B[u].sum());
}
int light[maxn];
void Insert(int x){
int v;
A.del(B[x].sum());
B[x].ins(0);
A.ins(B[x].sum());
for (int u = x; pre[u]; u = pre[u]){
v = pre[u];
A.del(B[v].sum());
B[v].del(C[u].top());
C[u].ins(dis(x,v));
B[v].ins(C[u].top());
A.ins(B[v].sum());
}
}
void Delete(int x){
int v;
A.del(B[x].sum());
B[x].del(0);
A.ins(B[x].sum());
for (int u = x; pre[u]; u = pre[u]){
v = pre[u];
A.del(B[v].sum());
B[v].del(C[u].top());
C[u].del(dis(x,v));
B[v].ins(C[u].top());
A.ins(B[v].sum());
}
}
int main(){
bin[0] = 1; for (int i = 1; i <= 25; i++) bin[i] = bin[i - 1] << 1;
Log[0] = -1; for (int i = 1; i < maxm; i++) Log[i] = Log[i >> 1] + 1;
n = read();
for (int i = 1; i < n; i++)
build(read(),read());
dfs(1);
for (int j = 1; j <= 18; j++)
for (int i = 1; i <= cnt; i++){
if (i + bin[j] - 1 > cnt) break;
mn[i][j] = min(mn[i][j - 1],mn[i + bin[j - 1]][j - 1]);
}
F[rt = 0] = INF; sum = n;
getrt(1);
solve(rt);
int m = read(),v; char opt;
while (m--){
opt = getchar();
while (opt != 'G' && opt != 'C') opt = getchar();
if (opt == 'G'){
v = A.top();
printf("%d\n",v >= 0 ? v : -1);
}
else {
v = read();
light[v] ^= 1;
if (!light[v]) Insert(v);
else Delete(v);
}
}
return 0;
}

BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】的更多相关文章

  1. BZOJ1095 [ZJOI2007]Hide 捉迷藏 动态点分治 堆

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ1095.html 题目传送门 - BZOJ1095 题意 有 N 个点,每一个点是黑色或者白色,一开始所 ...

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

    [BZOJ1095][ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉 ...

  3. 【bzoj1095】[ZJOI2007]Hide 捉迷藏 动态点分治+堆

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

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

    好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状( ...

  5. BZOJ1095:[ZJOI2007]Hide 捉迷藏(动态点分治)

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

  6. BZOJ 1095: [ZJOI2007]Hide 捉迷藏 动态点分治+堆

    写了7k多,可以说是一己之力切掉了这道毒瘤题~ 开 $3$ 种堆,分别维护每个子树最大深度,以及每个节点在点分树中对父亲的贡献,和全局的最优解. 由于需要支持堆的删除,所以写起来特别恶心+麻烦. 细节 ...

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

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

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

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

  9. BZOJ 1095 [ZJOI2007]Hide 捉迷藏 ——动态点分治

    [题目分析] 这题好基啊. 先把分治树搞出来.然后每个节点两个堆. 第一个堆保存这个块里的所有点(即分治树中的所有儿子)到分治树上的父亲的距离. 第二个堆保存分治树子树中所有儿子第一个堆的最大值. 建 ...

  10. BZOJ 1095: [ZJOI2007]Hide 捉迷藏(动态点分治)

    传送门 解题思路 点分树其实就是在点分治的基础上,把重心连起来.这样树高是\(log\)的,可以套用数据结构进行操作.这道题是求最远距离,所以每个点维护两个堆,分别表示所管辖的子树的最远距离和到父节点 ...

随机推荐

  1. Python实现多属性排序

    Python实现多属性排序 多属性排序:假如某对象有n个属性,那么先按某规则对属性a进行排序,在属性a相等的情况下再按某规则对属性b进行排序,以此类推. 现有对象Student: class Stud ...

  2. Python学习:for 循环 与 range()函数

    for 循环   For … in 语句是另一种循环语句,其特点是会在一系列对象上进行迭代(Iterates),即它会遍历序列中的每一个项目 注意:  1.else 部分是可选的.当循环中包含它时,它 ...

  3. yum方式安装及配置最新的mysql5.7

    1.删除旧版本的MySQL rpm -qa|grep -i mysql 用命令yum -y remove mysql 2.下载新版安装源 下载mysql的repo源 这个安装的mysql5.7.20 ...

  4. POJ2186 强连通分量+缩点

    Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 40234   Accepted: 16388 De ...

  5. (数据科学学习手札18)二次判别分析的原理简介&Python与R实现

    上一篇我们介绍了Fisher线性判别分析的原理及实现,而在判别分析中还有一个很重要的分支叫做二次判别,本文就对二次判别进行介绍: 二次判别属于距离判别法中的内容,以两总体距离判别法为例,对总体G1,, ...

  6. KMP算法(查找子序列)

    KMP类似暴力,但是不会和暴力完全一样,回溯到起点. 简单的说  假如   模板链字符串是:        abcabcabcabd        寻找abcabd 在模板链出现的次数,并且输出该次数 ...

  7. 【转】让Moodle支持多个域名

    默认情况下,moodle仅能绑定一个域名.但是由于学校网络分内网和外网,总希望如果是外网访问的,用外网的域名,用内网访问的,就转到内网的ip.这样访问的速度会更快一些,也减低对防火墙的压力.尤其是当外 ...

  8. IDLE激活方法

    激活流程 一.通过Activation code 方式激活, 注册码获取地址为:http://idea.lanyus.com/ 在idea或者pycharm的Activation code中输入 注册 ...

  9. Develop Android Game Using Cocos2d-x

    0. Environment Windows 7 x64Visual Studio 2013adt-bundle-windows-x86 (http://developer.android.com/s ...

  10. 使用polarssl进行RSA加密解密

    RSA算法的原理就不提了,网上有很多介绍的文章,因为项目中使用RSA加密,所以需要找一个RSA加密的算法,之前尝试过使用Crypto++库,无奈Crypto++其中使用了大量的模版,各种继承,看着头大 ...