【Codeforces 1083C】Max Mex(线段树 & LCA)
Description
给定一颗 \(n\) 个顶点的树,顶点 \(i\) 有点权 \(p_i\)。其中 \(p_1,p_2,\cdots, p_n\) 为一个 \(0\sim (n-1)\) 的一个排列。
有 \(q\) 次操作,每次操作:
- \(\texttt{1 x y}\):交换 \(x, y\) 两顶点的点权;
- \(\texttt{2}\):求树上所有路径上的点权构成的集合的 \(\text{MEX}\) 中最大的。其中 \(S\) 集合的 \(\text{MEX}\) 为其中最小的没有在 \(S\) 中出现过的自然数。
Hint
\(1\le n, q\le 2\times 10^5\)。
Solution
线段树与树的船新结合,orz。
在所有路径的 \(\text{MEX}\) 中,若出现过 \(\text{MEX}=x\),那么说明存在一条路径,上面出现了 \(0, 1, \cdots, (x-1)\) 所有这些点权。考虑到题目中 \(p\) 为一个排列,那么一个特定的点权只存在一个点与之对应,这给我们提供的很大的方便。
维护 \(\text{MEX}\),根据经验可以考虑对权值建立线段树。然后我们发现满足 \(\text{MEX} = x\) 的极小路径最多只有一条,于是可以让线段树的一个结点直接维护一条路径。具体来讲,对于值域区间 \([l, r]\) 的线段树结点,维护一条路径 \(s\to t\) 满足路径上包含了 \(l, (l+1), \cdots , r\) 所有这些权值,并且 \(s\to t\) 是最小的满足这个条件的路径。
如何实现两个结点的合并?假设左右儿子对应的路径分别为 \(s_l\to t_l, s_r\to t_r\),然后我们需要找到最小的一条同时包含这两条路径的路径,当然要注意可能不存在。
既然最小,毫无疑问就是取 \(s_l, s_r, t_l, t_r\) 这四个端点的其二作为新端点 \(s, t\),其余两个都必须位于路径 \(s\to t\) 上。于是枚举这两个小端点就完事了,反正是常数级别。
接下来只要做到快速判断一个点 \(x\) 是否位于路径 \(s\to t\) 即可。首先 \(x\) 必须是顶点 \(s, t\) 至少其中一个的祖先(或本身),然后 \(x\) 还不得高于 \(\text{LCA}(s, t)\)。总之需要满足——
\]
由于这里求 \(\text{LCA}\) 的次数较多,于是尽量用较快的求法(ST 表,树剖)。
然后是查询。不难发现这玩意有可二分性,那么就二分答案 \(x\),然后判断包含 \([0, x)\) 的路径是否存在即可。但这样是一次 \(O(\log^2 n)\),虽说应该也可以但我们有 \(O(\log n)\) 的方法。
考虑线段树上二分,在这里我们还需要在线段树上走是,维护一下前面一段 \([0, x)\) 对应的路径 \(P\)。
具体地,对于一个结点,先看看自己当前值域下的路径,如果存在,那么尝试能不能将这个路径直接和 \(P\) 合并。如果可以我们就直接走掉。
不行的话,就考虑左区间是否存在覆盖整段左值域区间的路径。如果没有,那么右边自然不必考虑,因为前面不连续,后半再连续也毫无意义;反之如果左边有,那么再考虑右边,之后就是递归的事了。
于是这道题就 \(O((n+q)\log n)\) AC 了(LCA 使用 ST 表)。
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Codeforces 1083C Max Mex
*/
#include <array>
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <vector>
#include <utility>
using namespace std;
const int N = 4e5 + 5;
const int logN = 20;
inline int read() {
int x = 0; char c; while (!isdigit(c = getchar()));
do x = (x << 1) + (x << 3) + c - 48; while (isdigit(c = getchar()));
return x;
}
int n, m, fa[N];
int val[N], loc[N];
vector<int> adj[N];
namespace LCA {
int fir[N], dep[N], lg2[N], timer = 0;
pair<int, int> f[N][logN];
void dfs(int x) {
f[fir[x] = ++timer][0] = make_pair(dep[x] = dep[fa[x]] + 1, x);
for (auto y : adj[x]) if (y != fa[x]) dfs(y), f[++timer][0] = make_pair(dep[x], x);
}
void init() {
dfs(1), lg2[0] = -1;
for (int i = 1; i <= timer; i++) lg2[i] = lg2[i >> 1] + 1;
for (int j = 1; j < logN; j++) for (int i = 1; i + (1 << j) - 1 <= timer; i++)
f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
int get(int x, int y) {
if (x <= 0 || y <= 0) return 0;
x = fir[x], y = fir[y]; if (x > y) swap(x, y);
int t = lg2[y - x + 1];
return min(f[x][t], f[y - (1 << t) + 1][t]).second;
}
}
bool in_path(int s, int t, int x) {
int l = LCA::get(s, t);
return (x == LCA::get(s, x) || x == LCA::get(t, x))
&& (l == LCA::get(x, l));
}
typedef array<int, 2> Path;
const Path emp = {-1, -1};
namespace segt {
#define mid ((l + r) >> 1)
Path dat[N << 2];
Path merge(Path a, Path b) {
if (a == emp || b == emp) return emp;
for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) {
int s = a[i], t = b[j], p = a[i ^ 1], q = b[j ^ 1];
if (in_path(s, t, p) && in_path(s, t, q)) return {s, t};
}
if (in_path(a[0], a[1], b[0]) && in_path(a[0], a[1], b[1])) return a;
if (in_path(b[0], b[1], a[0]) && in_path(b[0], b[1], a[1])) return b;
return emp;
}
void build(int x, int l, int r) {
if (l == r) { dat[x] = {loc[l], loc[l]}; return; }
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
dat[x] = merge(dat[x << 1], dat[x << 1 | 1]);
}
void fix(int x, int l, int r, int p) {
if (l == r) { dat[x] = {loc[l], loc[l]}; return; }
(p <= mid) ? fix(x << 1, l, mid, p) : fix(x << 1 | 1, mid + 1, r, p);
dat[x] = merge(dat[x << 1], dat[x << 1 | 1]);
}
int query(int x, int l, int r, Path& p) {
if (dat[x] != emp) {
if (p == emp) { p = dat[x]; return r + 1; }
Path f = merge(p, dat[x]);
if (f != emp) { p = f; return r + 1; }
}
if (l == r) return l;
int ret = query(x << 1, l, mid, p);
if (ret <= mid) return ret;
else return query(x << 1 | 1, mid + 1, r, p);
}
#undef mid
}
signed main() {
n = read();
for (int i = 1; i <= n; i++) loc[val[i] = read()] = i;
for (int i = 2; i <= n; i++) adj[fa[i] = read()].push_back(i);
LCA::init(), segt::build(1, 0, n - 1);
m = read();
while (m--) {
int opt = read();
if (opt == 1) {
int x = read(), y = read();
swap(loc[val[x]], loc[val[y]]), swap(val[x], val[y]);
segt::fix(1, 0, n - 1, val[x]), segt::fix(1, 0, n - 1, val[y]);
} else {
Path tmp = emp;
printf("%d\n", segt::query(1, 0, n - 1, tmp));
}
}
}
【Codeforces 1083C】Max Mex(线段树 & LCA)的更多相关文章
- Codeforces 1083C Max Mex [线段树]
洛谷 Codeforces 思路 很容易发现答案满足单调性,可以二分答案. 接下来询问就转换成判断前缀点集是否能组成一条链. 我最初的想法:找到点集的直径,判断直径是否覆盖了所有点,需要用到树套树,复 ...
- Codeforces 1083C Max Mex
Description 一棵\(N\)个节点的树, 每个节点上都有 互不相同的 \([0, ~N-1]\) 的数. 定义一条路径上的数的集合为 \(S\), 求一条路径使得 \(Mex(S)\) 最大 ...
- CF1083C Max Mex 线段树
题面 CF1083C Max Mex 题解 首先我们考虑,如果一个数x是某条路径上的mex,那么这个数要满足什么条件? 1 ~ x - 1的数都必须出现过. x必须没出现过. 现在我们要最大化x,那么 ...
- 51 nod 1766 树上的最远点对(线段树+lca)
1766 树上的最远点对 基准时间限制:3 秒 空间限制:524288 KB 分值: 80 难度:5级算法题 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个 ...
- Buses and People CodeForces 160E 三维偏序+线段树
Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- [Codeforces 1199D]Welfare State(线段树)
[Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...
- csu 1798(树上最远点对,线段树+lca)
1798: 小Z的城市 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 60 Solved: 16[Submit][Status][Web Board] ...
- BZOJ 2588: Spoj 10628. Count on a tree-可持久化线段树+LCA(点权)(树上的操作) 无语(为什么我的LCA的板子不对)
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 9280 Solved: 2421 ...
随机推荐
- linux之NTP服务
1. NTP服务(网络时间协议) Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒) ...
- CSS属性(边框)
1.边框 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="U ...
- 基于 abp vNext 微服务开发的敏捷应用构建平台 - 框架分析
总体架构 本平台从技术上采用ABP vNext和.NET Core编写的微服务架构.客户端层主要以现代浏览器为主,适配了PC端和移动端的访问,采用API和应用程序进行交互,同时提供第三方使用的 ...
- 慢SQL优化:where id in (select max(id)...) 改为join后性能提升400倍
背景 有两张表,都是主键递增,类似于主表和明细表: statistics_apply:统计申请表,主键applyId,7万多条记录 statistics_apply_progress:统计申请进度表( ...
- Redis多线程原理详解
本篇文章为你解答一下问题: 0:redis单线程的实现流程是怎样的? 1:redis哪些地方用到了多线程,哪些地方是单线程? 2:redis多线程是怎么实现的? 3:redis多线程是怎么做到无锁的? ...
- Python多线程join和setDaemon区别与用法
一直没有太搞清楚join和setDaemon有什么区别,总是对于它们两个的概念很模糊,需要做个实验然后记录一下. 先说结论: join: 子线程合并到主线程上来的作用,就是当主线程中有子线程join的 ...
- yii2.0 curl的使用
yii2 curl的使用办法 get: use linslin\yii2\curl; public function actionCurl($value =0) { $url = 'http://ya ...
- python搭建本地共享文件服务器
1.安装python 去官网下载python最新版,然后安装配置好环境 2.运行命令 在终端上输入以下命令 python3 -m http.server 当你执行完这个命令的时候,你的电脑会监听 80 ...
- 基于Docker搭建pypi私有仓库
一.搭建 1.准备htpasswd.txt文件 该文件内容包含上传包至仓库时验证的用户名和密码 pip install htpasswd htpasswd -sc htpasswd.txt <u ...
- TCP/IP网络协议基础入门及实验
TCP/IP简介 提到网络协议栈结构,最著名的当属 OSI 七层模型,但是 TCP/IP 协议族的结构则稍有不同,它们之间的层次结构有如图对应关系: 可见 TCP/IP 被分为 4 层,每层承担的任务 ...