【线段树】【CF1083C】 Max Mex
Description
给定一棵有 \(n\) 个点的树,每个节点有点权。所有的点权构成了一个 \(0~\sim~n - 1\) 的排列。有 \(q\) 次操作,每次操作 \(1\) 为交换两个点的点权,操作 \(2\) 为查询 \(Mex(l)\) 值最大的 \(Mex(l)\) 值,其中 \(l\) 是树上的一条路径。定义一条路径 \(l\) 的 \(Mex\) 值 \(Mex(l)\) 为这条路径上最小的没有出现过的自然数
Input
第一行是一个整数 \(n\) 代表点数
下面一行是 \(n\) 个数字,代表每个点的点权,保证是一个 \(0~\sim n - 1\) 的排列
下面一行有 \(n - 1\) 个数字,第 \(i\) 个数字代表钦定 \(1\) 为根时第 \(i + 1\) 个点的父节点
下面一行是一个整数 \(q\),代表操作个数
下面 \(q\) 行,每行一组操作。
如果该行第一个数字为 \(1\),则后面有两个数字 \(x,y\),代表交换 \(x,y\) 的点权
否则该行有且仅有一个数字 \(2\),代表一次查询
Output
对每次查询输出一行一个整数作为答案
Hint
\(2~\leq~n~\leq~2~\times~10^5~,~1~\leq~q~\leq~2~\times~10^5\)
Solution
一道线段树维护图上信息的好题。
记得以前在某学堂学习时,PKU的HJC老师说线段树出现在高端算法竞赛中,除了扔上来一个序列维护一堆东西以外,如果可以用线段树解决,那么原因一定是这道题目的信息具有 可合并性。现在想想这大概就是一个比较恰当的例题了。
我们考虑这道题目,每次询问可以转化成查询一个最小的 \(k\) 使得 \([0,k)\) 都出现在同一条链上。这个 \(k\) 显然是满足二分性质的,于是我们只需要一个能够维护这树节点权值前缀的连通性的数据结构了。考虑如果相邻的两段权值区间的点可以被合并为一条链,那么这段权值区间并显然是合法的。我们考虑对每段区间维护过区间内所有值的链的两个端点。注意这条链必须包含区间中的所有权值但是不一定仅包含这些权值。例如对于区间 \([1,2]\),这条链包含 \(\{1,2,4\}\) 也是合法的。
考虑合并时大区间合法当且仅当从两个链的四个端点中选两个做新的端点,剩下两个点都在链上即可。参考 这道题 我们可以得到判断一个点是否在一条链上的方法,即:
记两端点为 \(u,v\),需要判定的点为 \(x\),\(LCA(u,v)~=~y\),则有:
\(x\) 在链 \((u,v)\) 上 \(\Leftrightarrow\) $$((LCA(u,x) == x\lorLCA(v,x) == x)~\land LCA(x,y) == y)$$
于是枚举一下两个端点,判定一下即可。
发现在合并时我们会大量用到求LCA的操作,倍增显然不够优秀,于是我们使用ST预处理从而做到 \(O(1)\) 查询。这样pushup的复杂度就被我们降到了 \(O(w)\),其中 \(w\) 为枚举端点的情况数,即 \(C_4^2~=~6\)。所以修改的复杂度为 \(O(w \log n)\)
考虑查询时,直接在线段树上二分,维护前缀链的连通性即可做到 \(O(w \log n)\)。
于是总复杂度 \(O((n + q)~w \log n)\),其中 \(w~=~6\),可以通过本题
Code
#include <cmath>
#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define putchar(o)\
puts("I am a cheater!")
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
template <typename T>
inline void ReadDb(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
if (ch == '.') {
ch = IPT::GetChar();
double base = 1;
while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
}
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
rg int top=0;
do {OPT::buf[++top] = char(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 2000010;
const int maxm = 4000010;
int n, q, vistime, t;
int MU[maxn], fa[maxn], sz[maxn], dfn[maxm], pre[maxn], ST[30][maxn], deepth[maxn], rmp[maxn], dre[maxn];
std::vector<int> son[maxn];
struct Tree;
void dfs(ci);
void Make_ST();
int ask(Tree*);
void buildpool();
int cmp(ci&, ci&);
int Get_Lca(ci, ci);
void update(Tree*, ci);
bool Is_Lca(ci, ci, ci);
void build(Tree*, ci, ci);
void check(Tree*, Tree*, Tree*);
struct Tree {
Tree *ls, *rs;
int l, r;
int v[2];
inline void pushup() {
if (!this->rs) {this->v[0] = this->ls->v[0]; this->v[1] = this->ls->v[1];}
else if (!this->ls) {this->v[0] = this->rs->v[0]; this->v[1] = this->rs->v[1];}
else if (!(~this->ls->v[0])) {
this->v[0] = this->v[1] = -1;
} else if (!(this->rs->v[0])) {
this->v[0] = this->v[1] = -1;
} else {
check(this->ls, this->rs, this);
}
}
};
Tree *pool[maxm], qwq[maxm], *rot, pree;
int top;
int main() {
freopen("1.in", "r", stdin);
qr(n);
for (rg int i = 1; i <= n; ++i) {qr(MU[i]); ++MU[i];}
for (int i = 2; i <= n; ++i) {
qr(fa[i]); son[fa[i]].push_back(i); ++sz[fa[i]]; ++dre[fa[i]]; ++dre[i];
}
bool _flag = true;
for (rg int i = 1; i <= n; ++i) _flag &= dre[i] <= 2;
if (_flag) {
qr(q);
int a;
while (q--) {
a = 0; qr(a);
if (a == 2) {
qw(n, '\n', true);
} else {
qr(a); qr(a);
}
}
return 0;
}
dfs(1);
Make_ST();
for (rg int i = 1; i <= n; ++i) rmp[MU[i]] = i;
buildpool();
build(rot, 1, n);
qr(q); int a, b, c;
while (q--) {
a = 0; qr(a);
if (a == 2) {
pree.v[1] = pree.v[0] = 0; qw(ask(rot) - 1, '\n', true);
}
else {
b = c = 0; qr(b); qr(c);
std::swap(rmp[MU[b]], rmp[MU[c]]); std::swap(MU[b], MU[c]);
update(rot, MU[b]); update(rot, MU[c]);
}
}
return 0;
}
void dfs(ci u) {
dfn [pre[u] = ++vistime] = u;
for (int i = 0; i < sz[u]; ++i) {
deepth[son[u][i]] = deepth[u] + 1;
dfs(son[u][i]); dfn[++vistime] = u;
}
}
void Make_ST() {
int dn = n << 1;
t = log2(dn) + 1;
for (rg int i = 1; i < dn; ++i) ST[0][i] = dfn[i];
for (rg int i = 1; i <= t; ++i) {
rg int di = i - 1;
for (rg int l = 1; l < dn; ++l) {
int r= l + (1 << i) - 1;
if (r >= dn) break;
ST[i][l] = cmp(ST[di][l], ST[di][l + (1 << di)]);
}
}
}
int cmp(ci &_a, ci &_b) {
if (deepth[_a] < deepth[_b]) return _a;
return _b;
}
void buildpool() {
for (rg int i = 0; i < maxm; ++i) pool[i] = qwq + i;
rot = pool[maxm - 1]; top = maxm - 2;
}
void build(Tree *u, ci l, ci r) {
if ((u->l = l) == (u->r = r)) {u->v[1] = u->v[0] = rmp[l]; return;}
int mid = (l + r) >> 1;
if (l <= mid) build(u->ls = pool[top--], l, mid);
if (mid < r) build(u->rs = pool[top--], mid + 1, r);
u->pushup();
}
int Get_Lca(ci u, ci v) {
int l = pre[u], r = pre[v];
if (l > r) std::swap(l, r);
int len = r - l + 1, T = log2(len);
return cmp(ST[T][l], ST[T][r - (1 << T) + 1]);
}
bool Is_Lca(ci u, ci v, ci x) {
int _tmp = Get_Lca(u, v);
return (((Get_Lca(u, x) == x) || (Get_Lca(v, x) == x)) && (Get_Lca(_tmp, x) == _tmp));
}
#define doit \
if (Is_Lca(v1, u1, x) && (Is_Lca(v1, u1, y))) {\
u->v[0] = u1; u->v[1] = v1;\
return;\
}
void check(Tree *ls, Tree *rs, Tree *u) {
if (ls->v[0] == 0) { *u = *rs; return;}
int u1, v1, x, y;
{
u1 = ls->v[0]; v1 = ls->v[1]; x = rs->v[0]; y = rs->v[1];
doit;
}
{
u1 = rs->v[0]; v1 = ls->v[0]; x = rs->v[1]; y = ls->v[1];
doit;
}
{
u1 = rs->v[1]; v1 = ls->v[1]; x = rs->v[0]; y = ls->v[0];
doit;
}
{
u1 = rs->v[1]; v1 = ls->v[0]; x = rs->v[0]; y = ls->v[1];
doit;
}
{
u1 = rs->v[0]; v1 = ls->v[1]; x = rs->v[1]; y = ls->v[0];
doit;
}
{
u1 = rs->v[0]; v1 = rs->v[1]; x = ls->v[0]; y = ls->v[1];
doit;
}
u->v[0] = u->v[1] = -1;
}
#undef doit
void update(Tree *u, ci pos) {
if ((u->l > pos) || (u->r < pos)) return;
if (u->l == u->r) {u->v[0] = u->v[1] = rmp[pos]; return;}
if (u->ls) update(u->ls, pos);
if (u->rs) update(u->rs, pos);
u->pushup();
}
int ask(Tree *u) {
Tree _tmp;
if ((u->ls)) {
check(&pree, u->ls, &_tmp);
if (~_tmp.v[0]) {
pree = _tmp; return u->rs ? ask(u->rs) : u->r;
} else {
return ask(u->ls);
}
} else if (u->rs) return ask(u->rs);
else return u->r;
}
Summary
判断一个点在一条链上的方法为:它和链的其中一个端点的LCA是他自己且它和这条链顶端的节点的LCA是顶端节点。
当LCA查询极多且难以离线处理时,考虑使用ST预处理欧拉遍历序做到 \(O(1)\) 查询
注意ST存储欧拉遍历序需要开 \(2n\) 的空间
【线段树】【CF1083C】 Max Mex的更多相关文章
- CF1083C Max Mex 线段树
题面 CF1083C Max Mex 题解 首先我们考虑,如果一个数x是某条路径上的mex,那么这个数要满足什么条件? 1 ~ x - 1的数都必须出现过. x必须没出现过. 现在我们要最大化x,那么 ...
- [CF1083C]Max Mex
题目大意:有一棵$n(n\leqslant2\times10^5)$个点的树,每个点有点权,所有的点权构成了$0\sim n-1$的排列.$q(q\leqslant2\times10^5)$次操作,操 ...
- 【线段树】bzoj3585: mex
非常精妙的线段树题 Description 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三 ...
- 线段树-sum/max/min/区间更新
写一个板子. #include <cstdio> #include <algorithm> using namespace std; +; #define ROOT 1, 1, ...
- hdu 4747 线段树/DP
先是线段树 可以知道mex(i,i),mex(i,i+1)到mex(i,n)是递增的. 首先很容易求得mex(1,1),mex(1,2)......mex(1,n) 因为上述n个数是递增的. 然后使用 ...
- POJ——3264线段树
题目: 输入两个数(m,n),m表示牛的头数,n表示查询的个数.查询时输入两个数(x,y),表示查询范围的起始值和终止值,查询结果是,这个区间内牛重量的最大值减去牛重量的最小值,数量级为1000,00 ...
- Gym 102091K The Stream of Corning 2【线段树】
<题目链接> 题目大意: 进行两种操作:1.给定一个数的出现时间.价值.消失时间: 2.进行一次询问,问你当前时间,第K大的数的价值. 解题分析: 采用离线集中处理,将每个数的出现时间和它 ...
- hdu4719 Oh My Holy FFF 线段树优化dp
思路 好久之前的了,忘记什么题目了 可以到我这里做luogu 反正就是hdu数据太水,导致自己造的数据都过不去,而hdu却A了 好像是维护了最大值和次大值,然后出错的几率就小了很多也许是自己写错了,忘 ...
- HNCU专题训练_线段树(1)
1.内存控制2.敌兵布阵4.广告牌5.区间第k大数(模板题)6.just a Hook7.I Hate It8.动态的最长递增子序列(区间更新题)9.图灵树10.覆盖的面积14.买票问题16.村庄问题 ...
- P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并
LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...
随机推荐
- ORM PHP 学习记录
ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射.为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却 ...
- C++ 类 复制构造函数 The Copy Constructor
一.复制构造函数的定义 复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性.复制构造函数创建一个新的对象,作为另一个对象的拷贝.复制构造函数只含有一个形参,而且其形参为本类对象的引用.复制构 ...
- Beta冲刺第二周王者荣耀交流协会第五次会议
1.立会照片 成员:王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐. master:王磊 2.时间跨度: 2017年11月21日 15:00 — 15:17,总计17分钟. 3.地点: 一食堂 ...
- 用java构造一个带层次的文件目录遍历器
import java.util.List; import java.io.File; import java.util.ArrayList; public class IteratorUtil { ...
- “吃神么,买神么”的第二个Sprint计划(计划过程内容)
“吃神么,买神么”项目Sprint计划 ——6.1(第二天)立会内容与进度 团队组员各自任务: 陈键.吴舒婷:继续完善前台设局与布局 林欢雯.冯美欣:开展后台的界面的设计与布局 任务的进度: 陈键. ...
- 个人作业-week3案例分析
第一部分 软件调研测评(必应词典移动端) 找到的bug: 在词汇量测试中每个单词给用户思考的时间太短,只有五秒钟.导致很多似曾相识的单词还没来得及想起就已经过了.如果说测的是用户记忆深刻的单词,那些记 ...
- 使用pt-query-digest,找到不是很合适的sql
pt-query-digest 1. 概述 索引可以我们更快速的执行查询,但是肯定存在不合理的索引,如果想找到那些索引不是很合适的查询,并在它们成为问题前进行优化,则可以使用pt-query-dig ...
- java中的Serializable接口的作用
实现java.io.Serializable 接口的类是可序列化的.没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化. 序列化类的所有子类本身都是可序列化的.这个序列化接口没有任何方法和域, ...
- ajax 数据请求(一)同域
参考:http://www.css88.com/jqapi-1.9/jQuery.ajax/ http://www.cnblogs.com/haitao-fan/p/3908973.html 1.常用 ...
- [转帖]DAS、SAN、NAS
http://blog.itpub.net/26736162/viewspace-2214368/ DAS(Direct-attached Storage) 直连存储 直连式存储与服务器主机之间的连接 ...