传送门

如果没有修改显然就直接点分治

有修改那就动态点分治

动态点分治就是在点分树上维护一些东西,查询时也在点分树上查

因为点分树深度是$log$的所以可以保证时间复杂度

此题我们需要在点分树上维护 $c$ 和 $f$

$f[x]$ 维护节点 $x$ 的子树中传给它父亲 $Fa$ 的所有路径长度

$c[x]$ 维护节点 $x$ 的每一个儿子 $v$ 的 $f[v]$ 的最大值

那么询问的答案就是 max(每个节点的 $c[x]$ 的最大值与次大值之和)

那么对于维护每一个 $c[x]$ ,我们枚举 $x$ 的所有儿子 $v$,然后把每个 $f[v]$ 的最大值加入到 $c[x]$

至于维护 $f[x]$ 就是点分治的基本操作了

对于每一个修改操作,我们要把点分树上一条链的数据都进行修改,具体来说

如果是关灯,那么对于点 $x$ 这条链往上的每一个节点 $now$,要往 $f[now]$ 里插入一个 $dis[x][\ dep[now]\ ]$

($dep$存每个节点的深度,$dis[x][i]$ 表示节点 $x$ 这条链上深度为 $i$ 的点与 $x$ 的距离)

反之如果是开灯就要删除相应的距离

修改 $f$ 后相应的 $c$ 也要改变

实现 $f,c$ 的数据结构需要查询最大值,次大值和支持删除,容易想到用平衡树或者 $multiset$ 来维护

但是题解里有一种更骚的"数据结构",维护两个大根堆 $hp$ 和 $rub$,分别存值和删除的数

查询最大值和次大值就直接在 $hp$ 里面查,删除数的话就把要删除的数加到 $rub$ 里面

在 $hp$ 里取出堆顶的值时如果第一个数和 $rub$ 里的第一个数相同就把两个堆的堆顶同时弹掉直到出现不同或有一个堆空了

正确性十分的显然,如果数 $val$ 有被删除(即加入了 $rub$ 里),并且处在 $hp$ 的堆顶,那么 $val$ 也一定在 $rub$ 的堆顶(因为 $rub$ 里的数显然是 $hp$ 的子集)

全局的答案也直接用这个数据结构来维护就好了

代码有注释

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=2e5+,INF=1e9+;
int fir[N],from[N<<],to[N<<],cntt;
inline void add(int &a,int &b)
{
from[++cntt]=fir[a]; fir[a]=cntt;
to[cntt]=b;
}
struct Heap {//c和f的数据结构
priority_queue <int> hp,rub;
inline void ins(int x) { hp.push(x); } //插入
inline void del(int x) { hp.top()==x ? hp.pop() : rub.push(x); }//删除
inline int fir()//求最大值
{
while(!hp.empty()&&!rub.empty()&&hp.top()==rub.top()) hp.pop(),rub.pop();
return hp.empty() ? -INF : hp.top();//注意判断堆是否为空
}
inline int sec()//求次大值
{
int t=fir(),res; if(t==-INF) return t;
hp.pop(); res=fir(); hp.push(t);
return res;
}
}Ans,c[N],f[N];//Ans维护全局的答案
int n,m,tot,rt,cnt;
int sz[N],mx[N];
bool vis[N],p[N];//p是房间状态
void find_rt(int x,int fa)//找重心
{
sz[x]=; mx[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(vis[v]||v==fa) continue;
find_rt(v,x); sz[x]+=sz[v];
mx[x]=max(mx[x],sz[v]);
}
mx[x]=max(mx[x],tot-sz[x]);
if(mx[x]<mx[rt]) rt=x;
}
int Fa[N],dis[N][],dep[N],ans[N];//Fa是点分树上的父亲,ans[x]是点x的答案,用来维护Ans
int st[N],Top,Dep;//当前深度
void dfs(int x,int fa)
{
st[++Top]=x; dis[x][Dep]=dis[fa][Dep]+;//维护dis
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(v==fa||vis[v]) continue;
dfs(v,x);
}
}
void build(int x)//建树顺便预处理初始c,f
{
vis[x]=; dep[x]=dep[Fa[x]]+;//维护点分树的dep
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(vis[v]) continue;
tot=sz[v]; rt=; find_rt(v,); Dep=dep[x]+; Top=; dfs(v,);//dfs从v开始
for(int j=;j<=Top;j++) f[rt].ins(dis[st[j]][Dep]);//把子树的每个距离插入f[rt]
c[x].ins(f[rt].fir());//用点分树儿子更新当前节点 Fa[rt]=x; build(rt);//继续向下建树
}
c[x].ins();/*别忘了插一个0,本身到自己的距离也算*/ Ans.ins( ans[x]=c[x].fir()+c[x].sec() );//维护Ans并更新ans[x]
}
inline void change(int x)//修改操作,在点分树上维护数据
{
int now=x,d,t1,t2;
if(p[x]) cnt++,c[x].ins();//如果x为开灯,那么现在要关灯
else cnt--,c[x].del();//反之要开灯
while()
{
t1=c[now].fir()+c[now].sec();
if(t1!=ans[now]) Ans.del(ans[now]),Ans.ins(ans[now]=t1);//用c[now]更新Ans和ans[now]
if(!Fa[now]) break;
d=dep[now]; t1=f[now].fir();
p[x] ? f[now].ins(dis[x][d]) : f[now].del(dis[x][d]);//根据房间的状态判断是插入还是删除
t2=f[now].fir();
if(t1!=t2) c[Fa[now]].del(t1),c[Fa[now]].ins(t2);//如果状态有更新就用f[now]更新c[Fa[now]]
now=Fa[now];//往父亲跳
}
p[x]^=;//改变房间状态
}
int main()
{
int a,b; char ch[];
cnt=n=read();
for(int i=;i<n;i++)
{
a=read(),b=read();
add(a,b); add(b,a);
}
tot=n; mx[]=INF;
find_rt(,); build(rt);
m=read();
while(m--)
{
scanf("%s",ch);
if(ch[]=='G')
{
if(cnt==) printf("0\n");
else if(cnt==) printf("-1\n");
else printf("%d\n",Ans.fir());
}
else change(read());
}
return ;
}

P2056 [ZJOI2007]捉迷藏的更多相关文章

  1. 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告

    P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...

  2. 洛谷 P2056 [ZJOI2007]捉迷藏 题解【点分治】【堆】【图论】

    动态点分治入 门 题? 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由 \(N\) 个屋 ...

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

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

  4. Luogu P2056 [ZJOI2007]捉迷藏

    入坑动态点分治的题目,感觉还不错被卡常后重构代码 首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献. 这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找 ...

  5. 并不对劲的bzoj1095:p2056:[ZJOI2007]捉迷藏

    题目大意 给一\(n\)(\(n\leq10^5\))个点的一棵树,每个点有可能是黑色或白色,一开始所有点都是黑色,支持以下两种操作: 1.改变一个点的颜色 2.询问最远的黑色点对的距离 题解 据说是 ...

  6. [洛谷P2056][ZJOI2007]捉迷藏(2019-7-20考试)

    题目大意:有一棵$n(n\leqslant10^6)$个点的树,上面所有点是黑点,有$m$次操作: $C\;u$:把点$u$颜色翻转 $G$:问树上最远的两个黑点的距离,若没有黑点输出$0$ 题解:有 ...

  7. 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链

    树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...

  8. [ZJOI2007]捉迷藏(动态点分治/(括号序列)(线段树))

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

  9. [ZJOI2007] 捉迷藏

    idea1 可能会死掉的想法:考虑点分治维护每个分治中心x到达分治块内的个点距离,具体是用堆维护分治快内的x的儿子y到y的子树内的所有点距离(记为C[y]),取所有C[y]的top+e(x,y)放入x ...

随机推荐

  1. 在ubuntu12.04上安装6款顶级漂亮的BURG主题

    BURG 基本上是一个基于GRUB的Linux引导装载程序.BURG格有一个高度可配置的菜单系统,可选择文本和图形模式.简而言之,BURG可广泛定制,有良好免费的BURG主题.选择自己最喜欢的,下面我 ...

  2. Django--form验证及错误处理

    需求 from表单验证和ajax验证时返回的错误信息处理 速查 1.form表单提交时错误信息显示 views 1 2 error = form表单实例化对象.errors return render ...

  3. Luogu 1606 [USACO07FEB]白银莲花池Lilypad Pond

    感觉应当挺简单的,但是弄了好久……菜死了 如果不考虑那些为$1$的点,直接跑个最短路计数就好了,但是我们现在有一些边可以不用付出代价,那么只要在连边的时候先预处理搜一下就好了. 原来的想法是拆点,但是 ...

  4. CentOS7虚拟机安装Linux教程及安装后所遇到的问题

    1.VMware Workstation15下载. 官方链接:http://download3.vmware.com/software/wkst/file/VMware-workstation-ful ...

  5. Part7-时钟初始化_lesson1

    1.概念解析 1.1时钟脉冲信号 1.2时钟脉冲频率 1.3时钟源(提供时钟脉冲信号) a.晶振 b.锁相环PLL 2.时钟体系 2440: 晶振的频率.时钟体系有多少个PLL.这些PLL分别产生了哪 ...

  6. Python基础-2

    目录: 1.列表.元组操作 2.字符串操作 3.字典操作 4.集合操作 5.文件操作 6.字符编码与转码 一.列表.元组操作 定义列表 names = ['Freeman',"Jack&qu ...

  7. 第06章-渲染Web视图

    1. 理解视图解析 将控制器中请求处理的逻辑和视图中的渲染实现解耦是Spring MVC的一个重要特性.如果控制器中的方法直接负责产生HTML的话,就很难在不影响请求处理逻辑的前提下,维护和更新视图. ...

  8. 让Fireball CodeEditor控件禁止中文双倍输入

    第一次使用这个控件的时候,输入注释时候, 中文都是双倍输入,很是郁闷,查到资料,在 让Fireball CodeEditor控件支持中文 这篇文章中使用的方法,将代码复制过来发现不适用, 后来再一次偶 ...

  9. [python]模块及包

    一 .module 通常模块为一个文件,直接使用import来导入就好了.可以作为module的文件类型有".py".".pyo".".pyc&quo ...

  10. c++类 初始化另一对象

    Cbox类中对象a  可以直接赋值给对象b,无论类中数据成员是私有还是共有.且在创建a时调用了一次构造函数,b调用的是另外的默认构造函数: #include<iostream> using ...