P2056 [ZJOI2007] 捉迷藏

news/2024/11/15 23:53:22/文章来源:https://www.cnblogs.com/xcs123/p/18401515

题意:

给出一个 \(n\) 个点的树,每个点有黑白两种颜色。初始时每个点都是黑色的。\(q\) 次操作,支持:

  • C x 将第 \(x\) 个点的颜色反转。
  • G 询问树上两个黑色点的最远距离。

分析:

尝试使用点分树,对于一条路径,可以从点分树的 \(lca\) 处统计,由于涉及到删除和添加两种操作,因此可以用 multiset 来维护。

\(C_{i}\) 表示 \(i\)点分树子树内所有黑点到 \(i\) 的点分树父亲距离的可重集。因为需要保证每次的 \(C_{i}\) 都是不同子树,记 \(B_{i}\) 表示 \(i\)点分树儿子\(C_{j}\) 的可重集(特别的,如果 \(i\) 是黑色点,要把 \(0\) 也放进 \(B_{i}\) 里)。\(A\) 表示所有 \(B_{i}\) 的最大值和次大值之和的可重集(当然也有其他维护方法)。

由于 multiset 常数太大,使用双堆来模拟。具体地,加入一个数时放在堆 \(x\) 里,删除一个数时放在堆 \(y\) 里,查询最大值时,比较两个堆的堆顶,如果相同就同时弹出,直到堆顶不同为止,此时堆 \(x\) 的堆顶就是最大值。删除最大值同理。时间复杂度同样为 \(O(n + m \log^2 n)\)。只不过堆比 multiset 快了 \(1\) 倍。

代码:

#include<bits/stdc++.h>
#define N 100005 
using namespace std;int n, m, Q;
vector<int>p[N];int top[N], fa[N], dep[N], siz[N], son[N];
void dfs1(int x, int father) {fa[x] = father;dep[x] = dep[father] + 1;siz[x] = 1;int Maxson = -1;for(auto y : p[x]) {if(y == father) continue;dfs1(y, x);siz[x] += siz[y];if(siz[y] > Maxson) {Maxson = siz[y];son[x] = y;}}
}
void dfs2(int x, int topf) {top[x] = topf;if(son[x]) dfs2(son[x], topf);for(auto y : p[x]) {if(top[y]) continue;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;
}
int dis(int x, int y) {return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}int Fa[N], vis[N], root, Num, sum;
void Getroot(int x, int fa) {siz[x] = 1;int Maxsiz = 0;for(auto y : p[x]) {if(y == fa || vis[y]) continue; Getroot(y, x);siz[x] += siz[y];Maxsiz = max(Maxsiz, siz[y]);}Maxsiz = max(Maxsiz, sum - siz[x]);if(Maxsiz < Num) {Num = Maxsiz;root = x;}
}
void Build(int x) {vis[x] = 1;for(auto y : p[x]) {if(vis[y]) continue;Getroot(y, 0); //注意要先算出子连通块大小, 再去找重心 Num = 1e9, sum = siz[y];Getroot(y, 0);Fa[root] = x;Build(root);}
}int ljm[N], tot;struct Set { //双堆模拟set priority_queue<int>x, y;void Add(int a) { //加入a x.push(a);}void Del(int a) { //删除a y.push(a);}int Top() { //求最大值 while(y.size() && x.top() == y.top()) x.pop(), y.pop();return x.top();}void Pop() { //删除最大值 while(y.size() && x.top() == y.top()) x.pop(), y.pop();x.pop();}int Size() { //求集合大小 return x.size() - y.size();}int SecTop() { //求次大值 int A = Top(); Pop();int B = Top(); Add(A);return B;}
}C[N], B[N], A; //C[u]表示u子树内所有点距离Fa[u]的集合, B[u]表示u的儿子的C[u]的最大值的集合, A 表示 B[u]最大值和次大值 ;int Get_B(int x) {return B[x].Top() + B[x].SecTop();
}int Get_C(int x) {return C[x].Top();
}void upd(int x) {int i = x;if(B[i].Size() >= 2) A.Del(Get_B(i)); //删原来Aif(ljm[i] == 0) B[i].Add(0);else B[i].Del(0);if(B[i].Size() >= 2) A.Add(Get_B(i)); //加现在Awhile(Fa[x]) {if(B[Fa[x]].Size() >= 2) A.Del(Get_B(Fa[x])); //删原来Aif(C[x].Size()) B[Fa[x]].Del(Get_C(x)); //删原来Bif(ljm[i] == 0) C[x].Add(dis(i, Fa[x])); else C[x].Del(dis(i, Fa[x])); //更新Cif(C[x].Size()) B[Fa[x]].Add(Get_C(x)); //加现在Bif(B[Fa[x]].Size() >= 2) A.Add(Get_B(Fa[x])); //加现在Ax = Fa[x];}if(ljm[i] == 0) tot++;else tot--;ljm[i] ^= 1;
}int query() {if(tot == 0) return -1;else if(tot == 1) return 0;else return A.Top();
}void work() {for(int i = 1; i <= n; i++) upd(i);cin >> Q;while(Q--) {char opt;cin >> opt;if(opt == 'C') {int x;cin >> x;upd(x);}else cout << query() << endl;}
}signed main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n;for(int i = 1, u, v; i < n; i++) {cin >> u >> v;p[u].push_back(v);p[v].push_back(u); }dfs1(1, 0);dfs2(1, 1);Num = 1e9, sum = n; Getroot(1, 0);Build(root);work();return 0;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/793553.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数学基础讲解-01

真题:易 设K法 消元的过程真题:2015 不定方程问题 必考 找到特征 解题入口 真题: 重点 没做出来一道题目,多解几次性质需要理解记忆 真题: 重点!!! 有陷阱、筛选作用 做错了 没考虑为0的情况 比较喜欢考细节 常见的坑不能约 需要移项一道题目做个三五遍不算多…

CF1945F Kirill and Mushrooms

题意 营地里的人一睡着,基里尔就偷偷溜出帐篷,去智者橡树那里采蘑菇。 众所周知,橡树下生长着 \(n\) 种蘑菇,每种蘑菇都有 \(v_i\) 的魔力。基里尔非常想用这些蘑菇制作一种魔力最大的灵药。 灵药的强度等于其中蘑菇的数量与这些蘑菇中最小魔力的乘积。为了配制灵药,基里尔…

我发布了一款相亲平台《i相遇》

因缘际会之下,我踏入了相亲平台的领域。起初,是为一位客户打造专属相亲应用,过程中深入体验了众多同类平台,却遗憾地发现它们普遍掺杂着欺诈的阴影——高昂的费用、兼职托儿的身影、以及虚假的钓鱼信息,不一而足。 完成客户项目后,我决定自己运营一款专为互联网人量身打造…

Linux系统安装ansiblle环境

前言:这里是生产环境红帽系统,如果是别的系统,本地源的配置方法不一样。 一、主节点服务器Redhat8 配置使用阿里源: 备份:sudo /etc/yum.repos.d/CentOS-Base.repo /opt/yum/ 下载新的CentOS-Base.repo 到 /etc/yum.repos.d/ (这里用的CentOS 8.0) 命令:sudo wget -O /…

1.hadoop入门

Hadoop入门一.概念1.hadoop是什么 (1)Hadoop是一个由apache基金会所开发的分布式系统基础架构(2)主要解决,海量数据的存储和海量数据的分析计算问题(3)广义上来说,hadoop通常是指一个更广泛的概念--hadoop生态圈2.Hadoop发展历史(1)创始人Doug Cutting,为了实现与Google类似的全…

JQuery的DOM操作

JQuery基础教程第四版第五章:DOM操作方法的简单归纳 使用JQuery的DOM操作实现以下功能1.创建新元素2.插入新元素3.移动元素4.包装元素5.复制元素 相关代码 HTML文件<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"&g…

C++顺序结构(1)

1、C++程序的样子2、流 输出流 COUT<<3、一个实例及解析 // 001 程序的基本结构 //单行注释 /*多行注释 被注释过的内容不会被运行,可以用来做笔记。 基本结构: 1.头文件 程序包含某个头文件后,程序中的代码就可以使用这个头文件里的功能。 2.命名空间 3.主函数 类…

manim边学边做--角度标记

manim中绘制一个角度其实就是绘制两条直线,本篇介绍的不是绘制角度,而是绘制角度标记。 对于锐角和钝角,角度标记是一个弧,弧的度数与角的度数一样; 对于直角,角度标记是一个垂直的拐角。 manim中关于角度标记的模型主要有3个:Angle:根据两条直线绘制角度标记 RightAng…

关于Tailscale Subnet routers要说的

国内的水文很多,Tailscale的部署就不说了。简单的都有讲到,但凡深度一点儿的只能找找外边儿的文章了。 昨天刚给群晖装完Tailscale,打算着利用Subnet routers功能来访问另外的2个子网。 国内的水文在介绍这一段使用的时候是这样的:该文章提到:如果有多个网段添加,就多运行…

CASIA-OLHWDB1.0-1.2数据集解析

OLHWDB1.0-1.2提供联机手写单字数据。 OLHWDB1.0收录汉字3866个,字母数字及符号171个。其中,GB2312-80一级集(共3755个字符)收录汉字3740个。 OLHWDB1.1收录GB2312-80一级集汉字3755个,字母数字及符号171个。 OLHWDB1.2收录汉字3319个,字母数字及符号171个。OLHWDB1.2的汉…

枚举测试

/*** 枚举类*/ public enum SocialTypeEnum {FACEBOOK(1),//脸书GITEE(2),//GITEEWECHAT_ENTERPRISE(3);//企业微信 <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">final</span> <span style=&…

ROS话题通信

# 发布方 # 以发布订阅的方式实现不同节点之间数据交互的通讯模式,用于不断更新的、少逻辑处理的数据传输场景 impoosyt rospy from std_msgs.msg import String# 初始化ROS节点(命门) rospy.init_node("talker_1") # 实例化发布者对象 pub_1 = rospy.Publisher(&…