【ZJOI2007】捉迷藏
题面
Description
Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。
某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。
他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,
这N-1条走廊的分布使得任意两个屋子都互相可达。
游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。
在起初的时候,所有的灯都没有被打开。
每一次,孩子们只会躲藏在没有开灯的房间中,
但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。
为了评估某一次游戏的复杂性,
Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。
我们将以如下形式定义每一种操作:
Input
输入文件hide.in第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。
接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。
接下来一行包含一个整数Q,表示操作次数。.
接着Q行,每行一个操作,如上文所示。
Output
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。
若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。
Sample Input
8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
Sample Output
4
3
3
4
Hint
对于20%的数据,N≤50, Q≤100;
对于60%的数据,N≤3000, Q≤10000;
对于100%的数据,N≤100000, Q≤500000。
题目分析
动态询问多组点对信息,所以使用动态点分治。
根据点分治的基本思路,
对于一个重心,我们通常记录经过重心的路径来统计答案。
这里我们沿用这种思路,设亮着的房间为白点,黑着的房间为黑点。
对于某个重心\(w\),记录每个子树\(v\)中的黑点到\(w\)的最长链(每个子树只能贡献一个,避免重复)。
计算该重心对答案的贡献时直接取堆\(c[w]\)中的最大值+次大值即可。
由于有删除操作,我们要能够迅速更新最长链的方法。
于是,我们对每个节点\(v\),再开一个堆\(up\),记录以\(v\)为根的子树到\(v\)的\(parent\)的最长链。
注意:
上面的\(parent\)指的是点分树上的父亲。
然后再维护一个\(ans\)堆记录答案。
还有一个注意事项,由于要支持堆中的删除操作,需要额外建一个堆维护删除信息。
代码实现
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#include<queue>
#define MAXN 0x7fffffff
typedef long long LL;
const int N=100005;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
int h[N],cnt;
struct Edge{int to,next;}g[N<<1];
void AddEdge(int x,int y){g[++cnt].to=y,g[cnt].next=h[x],h[x]=cnt;}
int fa[N],dep[N];
int size[N],son[N],tp[N];
void Dfs1(int x){
size[x]=1;
for(int i=h[x];i;i=g[i].next){
int to=g[i].to;
if(to==fa[x])continue;
fa[to]=x,dep[to]=dep[x]+1;
Dfs1(to),size[x]+=size[to];
if(size[to]>size[son[x]])son[x]=to;
}
}
void Dfs2(int x,int top){
tp[x]=top;
if(son[x])Dfs2(son[x],top);
for(int i=h[x];i;i=g[i].next){
int to=g[i].to;
if(to==fa[x]||to==son[x])continue;
Dfs2(to,to);
}
}
int LCA(int x,int y){
while(tp[x]^tp[y]){
if(dep[tp[x]]<dep[tp[y]])y=fa[tp[y]];
else x=fa[tp[x]];
}
return dep[x]<dep[y]?x:y;
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[LCA(x,y)];}
struct Heap{
priority_queue<int>q,del;
void push(int x){q.push(x);}
void erase(int x){del.push(x);}
int top(){
while(del.size()&&q.top()==del.top())q.pop(),del.pop();
return q.top();
}
void Pop(){
while(del.size()&&q.top()==del.top())q.pop(),del.pop();
q.pop();
}
int sec(){
int tmp=top();Pop();
int x=top();push(tmp);
return x;
}
int size(){return q.size()-del.size();}
}c[N],up[N],ans;
int n,num,sum,prt[N];
int rt,sz[N],mx[N];
bool vis[N],light[N];
void to_ans(int v,bool f){
if(c[v].size()>1){
int x=c[v].top()+c[v].sec();
f?ans.push(x):ans.erase(x);
}
}
void Explore(int x,int fa,int top){
up[top].push(dis(x,prt[top]));
for(int i=h[x];i;i=g[i].next){
int to=g[i].to;
if(vis[to]||to==fa)continue;
Explore(to,x,top);
}
}
void Get_size(int x,int fa) {
sz[x]=1;
for(int i=h[x];i;i=g[i].next) {
int to=g[i].to;
if(to==fa||vis[to]) continue;
Get_size(to,x);
sz[x]+=sz[to];
}
}
void Get_root(int x,int fa){
mx[x]=0;
for(int i=h[x];i;i=g[i].next){
int to=g[i].to;
if(to==fa||vis[to])continue;
Get_root(to,x);
mx[x]=max(mx[x],sz[to]);
}
mx[x]=max(mx[x],sum-sz[x]);
if(mx[rt]>mx[x])rt=x;
}
void Solve(int x){
vis[x]=1,c[x].push(0);
for(int i=h[x];i;i=g[i].next){
int to=g[i].to;
if(vis[to]||to==prt[x])continue;
Get_size(to,0),sum=sz[to];
rt=0,Get_root(to,0);
prt[rt]=x;
Explore(rt,0,rt);
c[x].push(up[rt].top());
Solve(rt);
}
to_ans(x,1);
}
void Change(int x,bool f){
f?(num--):(num++);
to_ans(x,0);
f?c[x].erase(0):c[x].push(0);
to_ans(x,1);
for(int i=x;prt[i];i=prt[i]){
to_ans(prt[i],0);
if(up[i].size())c[prt[i]].erase(up[i].top());
f?up[i].erase(dis(x,prt[i])):up[i].push(dis(x,prt[i]));
if(up[i].size())c[prt[i]].push(up[i].top());
to_ans(prt[i],1);
}
}
int main(){
num=n=Getint();
for(int i=1;i<n;i++){
int x=Getint(),y=Getint();
AddEdge(x,y),AddEdge(y,x);
}
dep[1]=1,Dfs1(1),Dfs2(1,1);
sum=n,mx[0]=n+1;
Get_size(1,0),Get_root(1,0);
Solve(rt);
int m=Getint();
for(int i=1;i<=m;i++){
char ch=getchar();
while(ch!='C'&&ch!='G')ch=getchar();
if(ch=='C'){
int x=Getint();
light[x]^=1;
Change(x,light[x]);
}else{
if(!num)cout<<"-1\n";
else if(num==1)cout<<"0\n";
else cout<<ans.top()<<'\n';
}
}
return 0;
}
【ZJOI2007】捉迷藏的更多相关文章
- 洛谷 P2056 [ZJOI2007]捉迷藏 解题报告
P2056 [ZJOI2007]捉迷藏 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由\ ...
- 树上最长链 Farthest Nodes in a Tree LightOJ - 1094 && [ZJOI2007]捉迷藏 && 最长链
树上最远点对(树的直径) 做法1:树形dp 最长路一定是经过树上的某一个节点的. 因此: an1[i],an2[i]分别表示一个点向下的最长链和次长链,次长链不存在就设为0:这两者很容易求 an3[i ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 题解【点分治】【堆】【图论】
动态点分治入 门 题? 题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由 \(N\) 个屋 ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV
意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...
- [ZJOI2007]捉迷藏(动态点分治/(括号序列)(线段树))
题目描述 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条 ...
- [ZJOI2007] 捉迷藏
idea1 可能会死掉的想法:考虑点分治维护每个分治中心x到达分治块内的个点距离,具体是用堆维护分治快内的x的儿子y到y的子树内的所有点距离(记为C[y]),取所有C[y]的top+e(x,y)放入x ...
- Luogu P2056 [ZJOI2007]捉迷藏
入坑动态点分治的题目,感觉还不错被卡常后重构代码 首先静态点分治相信大家肯定都会,就是不断找重心然后暴力计算每棵子树内的贡献. 这题如果只有单次询问,我们很容易想到对于每个分治中心的所以儿子的子树中找 ...
- BZOJ.1095.[ZJOI2007]捉迷藏(线段树 括号序列)
BZOJ 洛谷 对树DFS得到括号序列.比如这样一个括号序列:[A[B[E][F[H][I]]][C][D[G]]]. 那比如\(D,E\)间的最短距离,就是将\(D,E\)间的括号序列取出:][[] ...
- 【bzoj1095】 ZJOI2007—捉迷藏
http://www.lydsy.com/JudgeOnline/problem.php?id=1095 (题目链接) 题意 一棵树,求最远的两黑点之间的距离,每次可以将黑点染白或者将白点染黑. So ...
- P2056 [ZJOI2007]捉迷藏
传送门 如果没有修改显然就直接点分治 有修改那就动态点分治 动态点分治就是在点分树上维护一些东西,查询时也在点分树上查 因为点分树深度是$log$的所以可以保证时间复杂度 此题我们需要在点分树上维护 ...
随机推荐
- opensns功能详解
<!DOCTYPE html> opensns功能详解 wmd-preview h1 { color: #0077bb; /* 将标题改为蓝色 */ } opensns功能详解 软件工程 ...
- Stm32CubeMX5 创建LED控制工程 - 基于stmf051k8u6
一. 创建一个控制LED的工程 1. 安装好 Stm32CubeMX5 后 打开软件 选择 “ File--> New Project...” 创建一个新工程 2. 之后会出现一个选择芯片的窗 ...
- 第一章 笔记本电脑安装Linux系统(Centos7)
目标:通过[Linux+Docke+Nginx+Jenkins+k8s(Kubernetes)+CICD(自动化)]进行项目部署 内容:根据个人进度实时分章节记录自己所遇到的问题 一.准备工作 1.下 ...
- gradle笔记
gradle笔记 一.基础知识 1.1.groovy语言 Groovy适用于Java虚拟机的一种敏捷的动态语言,他是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言,使用该 ...
- 前端学习(一)html标签(笔记)
html->标签 标题标签:<h1>标题文字</h1>段落标签:<p>段落文字</p>换行标签:<br/>图片标签:<img s ...
- 【读书笔记】剑指offer
导语 所有的编程练习都在牛客网OJ提交,链接: https://www.nowcoder.com/ta/coding-interviews 九章算法的 lintcode 也有这本书的题目.https: ...
- 6、Python 中 利用 openpyxl 读 写 excel 操作
__author__ = 'Administrator' from openpyxl import load_workbook # Excel_Util 类 class Excel_util: #初始 ...
- demo BaseDao随笔,hibernate框架
/** * 增加entity * * @param Object对象 * @throws Exception */ public <T> void save(T ob) throws Ex ...
- BZOJ 3230: 相似子串(后缀数组)
传送门 解题思路 其实题目挺好想的.首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名.这样就可以预处理出每个后缀的\(l,r ...
- STM32嵌入式开发学习笔记(一)
本文中,笔者将介绍使用嵌入式开发工具Keil uVision5,使用C语言,对微处理器STM32F103C8进行嵌入式开发. 开发使用C语言,首先需要新建一个C语言文件,将其设为主函数的入口,因此,将 ...