P2056 [ZJOI2007] 捉迷藏 题解

news/2024/10/15 11:35:29/

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/news/1539412.html

相关文章

【Vue】Vue3.0(十一)Vue 3.0 中 computed 计算属性概念、使用及示例

上篇文章&#xff1a;【Vue】Vue3.0&#xff08;十&#xff09;toRefs()和toRef()的区别及使用示例 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年10月15日10点23分 文章目录 Vue 3.0中…

19 Shell Script awk命令

Shell Script awk命令 一、awk 一&#xff09;awk介绍 ​ awk是一个强大的文本分析工具&#xff0c;相对于grep的查找&#xff0c;sed的编辑&#xff0c;awk在其对数据分析并生成报告时&#xff0c;显得尤为强大。简单来说awk就是把文件逐行的读入&#xff0c;以空格为默认分…

docker方式k8s环境搭建及pod简介

目录 一 搭建环境准备 二 搭建步骤 三 pod简介 3.1 pod介绍 3.2 pod配置 3.3 pod生命周期 3.4 pod调度 一 搭建环境准备 1.1 关闭防火墙和selinux&#xff0c;有自己搭建好的harbor仓库 1.2 先禁用服务器的交换分区&#xff08;如果服务器内存不够&#xff0c;k8s使用交…

理解Token和Session:鉴权与会话管理的区别

理解Token和Session&#xff1a;鉴权与会话管理的区别 在Web应用和API设计中&#xff0c;鉴权与会话管理是两个核心概念&#xff0c;它们对于确保用户身份的安全性和维护用户会话状态至关重要。Token和Session是两种常用的鉴权与会话管理机制&#xff0c;它们各自具有独特的工…

揭秘网络流量分析系统:保障IT运维稳定的幕后英雄

AnaTraf 网络性能监控系统NPM | 全流量回溯分析 | 网络故障排除工具 在现代企业的IT运维中&#xff0c;保障网络的稳定运行和业务的连续性是重中之重。网络流量的监控和优化不仅能提升网络性能&#xff0c;还能帮助及时发现潜在故障&#xff0c;防止网络瘫痪。因此&#xff0c…

UE5运行时动态加载场景角色动画任意搭配-场景角色相机动画音乐加载方法(三)

1、将场景打包为Pak并加载 1、参考这篇文章将场景打包为pak,UE4打包并加载Pak-Windows/iOS/Android不同平台Editor/Runtime不同运行模式兼容 2、在Mount Pak后直接打开Map即可 void UMapManager::OpenMap(FString Path) {UWorld* World = UGlobalManager::GetInstance()->…

如何在uniAPP中编写页面

在uni-app中编写页面主要涉及到创建Vue组件文件&#xff08;通常以.vue为扩展名&#xff09;&#xff0c;并在这些文件中编写模板&#xff08;template&#xff09;、脚本&#xff08;script&#xff09;和样式&#xff08;style&#xff09;。以下是一个基本的步骤指南&#x…

SpringBoot智能推荐:健康生活新体验

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…