P2056 [ZJOI2007] 捉迷藏 题解

ops/2024/10/15 12:34:02/

P2056 [ZJOI2007] 捉迷藏

题面:

题目描述

Jiajia 和 Wind 是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind 和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由 N N N 个屋子和 N − 1 N-1 N1 条双向走廊组成,这 N − 1 N-1 N1 条走廊的分布使得任意两个屋子都互相可达。

游戏是这样进行的,孩子们负责躲藏,Jiajia 负责找,而 Wind 负责操纵这 N N N 个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia 希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。

我们将以如下形式定义每一种操作:

  • C(hange) i 改变第 i i i 个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
  • G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
输入格式

第一行包含一个整数 N N N,表示房间的个数,房间将被编号为 1 , 2 , 3 … N 1,2,3…N 1,2,3N 的整数。

接下来 N − 1 N-1 N1 行每行两个整数 a a a, b b b,表示房间 a a a 与房间 b b b 之间有一条走廊相连。

接下来一行包含一个整数 Q Q Q,表示操作次数。接着 Q Q Q 行,每行一个操作,如上文所示。

输出格式

对于每一个操作 Game,输出一个非负整数,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出 0;若所有房间的灯都开着,输出 -1

样例 #1
样例输入 #1
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
样例输出 #1
4
3
3
4
提示

对于 20 % 20\% 20%的数据, N ≤ 50 N \leq 50 N50, M ≤ 100 M\leq 100 M100

对于 60 % 60\% 60%的数据, N ≤ 3000 N \leq 3000 N3000, M ≤ 10000 M \leq 10000 M10000

对于 100 % 100\% 100%的数据, N ≤ 100000 N \leq 100000 N100000, M ≤ 500000 M \leq 500000 M500000

出现 & 消失!

太棒了,线段树分治

我们建立时间线段树,将每个点的出现到消失时间插入线段树里面。

然后是结论时间!

结论:当树中新加入一个节点 x x x,原先的 u ⇝ v u \leadsto v uv 直径,只会变成 u ⇝ x u \leadsto x ux x ⇝ v x \leadsto v xv

证明:若插入一个 x x x,如果原直径是 u ⇝ v u\leadsto v uv,而新直径是 x ⇝ y x\leadsto y xy,则 u ⇝ y u \leadsto y uy v ⇝ y v\leadsto y vy 肯定比 u ⇝ v u\leadsto v uv 更长,故矛盾!

那么我们要在栈中记录 u , v u,v u,v,然后通过上面的结论,更新 u , v u,v u,v,退出线段树节点时撤回退栈。

然后对每个时间点,记录答案。

求两点距离,考虑树剖 + 树上差分。

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
int rd() {int x = 0, w = 1;char ch = 0;while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9') {x = x * 10 + (ch - '0');ch = getchar();}return x * w;
}
void wt(int x) {static int sta[35];int f = 1;if(x < 0) f = -1,x *= f;int top = 0;do {sta[top++] = x % 10, x /= 10;} while (x);if(f == -1) putchar('-');while (top) putchar(sta[--top] + 48);
}
const int N = 5e5+5;
int head[N],nxt[N<<1],to[N<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v) {nxt[cnt] = head[u];to[cnt] = v;head[u] = cnt++;
}
int lst[N],n,q;
int fa[N],siz[N],top[N],son[N],dep[N];
void dfs1(int x,int f) {fa[x] = f;siz[x] = 1;dep[x] = dep[f] + 1;for(int i = head[x];~i;i = nxt[i]) {int y = to[i];if(y ^ f) {dfs1(y,x);siz[x] += siz[y];if(siz[son[x]] < siz[y]) son[x] = y;}}
}
void dfs2(int x,int topx) {top[x] = topx;if(!son[x]) return;dfs2(son[x],topx);for(int i = head[x];~i;i = nxt[i]) {int y = to[i];if(y ^ fa[x] && y ^ son[x]) dfs2(y,y);}
}
int lca(int x,int y) {while(top[x] ^ top[y]) {if(dep[top[x]] < dep[top[y]]) swap(x,y);x = fa[top[x]];}return dep[x] < dep[y] ? x : y;
}
bitset<N> col,qb;
vector<int> t[N<<2];
stack<pii> st;
int u,v,ans[N];
int dis(int x,int y) {return dep[x] + dep[y] - 2 * dep[lca(x,y)];
}
#define mid ((pl + pr) >> 1)
#define ls (p << 1)
#define rs (ls | 1)
void insert(int p,int pl,int pr,int l,int r,int x) {if(l <= pl && pr <= r) {t[p].emplace_back(x);return;}if(l <= mid) insert(ls,pl,mid,l,r,x);if(r > mid) insert(rs,mid+1,pr,l,r,x);
}
void solve(int p,int pl,int pr) {auto now = st.size();for(int x : t[p]) {st.push({u,v});if(!u && !v) u = v = x;else {int d0 = dis(u,v),d1 = dis(u,x),d2 = dis(v,x);int d = max(d0,max(d1,d2));if(d == d1) v = x;else if(d == d2) u = x;}}if(pl == pr) ans[pl] = (!u || !v) ? -1 : dis(u,v);else solve(ls,pl,mid),solve(rs,mid+1,pr);while(st.size() != now) {auto t = st.top();u = t.first,v = t.second;st.pop();}
}signed main() {init();n = rd();for(int i = 1;i<n;i++) {int u = rd(),v = rd();add(u,v);add(v,u);}q = rd();dfs1(1,0);dfs2(1,1);for(int i = 1;i<=n;i++) lst[i] = 1; for(int i = 1;i<=q;i++) {char op = getchar();while(op != 'C' && op != 'G') op = getchar();if(op == 'C') {int s = rd();if(!col[s]) {col[s] = 1;insert(1,1,q,lst[s],i,s);}else col[s] = 0,lst[s] = i;}else qb[i] = 1;}for(int i = 1;i<=n;i++) if(!col[i]) insert(1,1,q,lst[i],q,i);solve(1,1,q);for(int i = 1;i<=q;i++)if(qb[i])wt(ans[i]),putchar('\n');return 0;
}

http://www.ppmy.cn/ops/125942.html

相关文章

C++AVL树详解

什么是AVL树 AVL树是最先发明的⾃平衡⼆叉查找树&#xff0c;AVL是⼀颗空树&#xff0c;或者具备下列性质的⼆叉搜索树&#xff1a;它的 左右⼦树都是AV树&#xff0c;且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树&#xff0c; 通过控制⾼度差去控制平衡…

SpringBoot+Vue+Uniapp智能社区服务小程序系统(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

优化 webpack 的打包速度的优化

前端面试题包括ECMScript,TypeScript,Nodejs,React,Webgl,Threejs等还在整理中&#xff0c;在线地址前端面试题&#xff0c;源码地址大家多多支持才有动力给大家分享更多好的面试题。 优化 Webpack 的打包速度可以显著提升开发效率&#xff0c;尤其是在大型项目中。以下是一些…

Go语言--快速入门

Go语言特点 我们先看一下简单的Go语言程序 package mainimport "fmt"func main() { // 错误&#xff0c;{ 不能在单独的行上fmt.Println("Hello, World!") }我们可以看到外型非常像我们的JAVA&#xff0c;但是不需要&#xff1b;作为结尾&#xff0c;…

低代码可视化-uniapp商城首页小程序-代码生成器

在设计一个小程序的首页时&#xff0c;包含轮播图、通知栏和商品列表这三个元素是非常常见且有效的布局方式。这样的设计既能够吸引用户的注意力&#xff0c;又能够高效地展示信息和商品。 轮播组件 小程序首页幻灯片通常位于小程序的顶部或显著位置&#xff0c;通过滑动屏幕可…

使用tgz包下载安装clickhouse低版本

1.下载安装包 官方下载地址&#xff1a;https://packages.clickhouse.com/tgz/stable 阿里云下载地址&#xff1a;clickhouse-tgz-stable安装包下载_开源镜像站-阿里云 共需要下载四个文件 clickhouse-common-static-20.3.10.75.tgz clickhouse-common-static-dbg-20.3.10.7…

Java利用itextpdf实现pdf文件生成

前言 最近公司让写一个数据页面生成pdf的功能&#xff0c;找了一些市面代码感觉都太麻烦&#xff0c;就自己综合性整合了一个便捷的工具类&#xff0c;开发只需简单组装数据直接调用即可快速生成pdf文件。望大家一起学习&#xff01;&#xff01;&#xff01; 代码获取方式&am…

3.Linux中安装redis及环境搭建

文章目录 1.在Ubuntu中安装redis2.在Centos中安装Redis 5(不建议&#xff0c;现在yum仓库已经停止维护)3.Ubuntu中安装mysql4.Ubuntu中安装java85.Ubuntu中启动Java程序6.环境搭建及介绍 大家好&#xff0c;我是晓星航。今天为大家带来的是 Linux中安装redis 相关的讲解&#x…