【ZROJ2730】简单题 可持久化分块题解
Description
给定一棵 \(n\) 个节点的树,每次询问编号为 \([l, r]\) 的点中有多少个是祖先关系。
\(n, q \le 10^5\)。
Solution
直接做的话树上的祖先关系不好统计,那么转化到 \(\texttt{dfs}\) 序上,如果 \(u\) 是 \(v\) 的祖先那么 \(dfn_u \le dfn_v < dfn_u + siz_u\)。
把 \([dfn_u, dfn_u + siz_u - 1]\) 看成一条线段,把树上的点拆成一条线段和一个点,那么问题就变成了用 \([l, r]\) 的线段覆盖 \([l, r]\) 的点,能够覆盖多少次。
首先对点分块,\(f_{i, j}\) 表示前 \(i\) 条线段在第 \(j\) 个块中能覆盖多少次,可以对每一个块中的点做一遍前缀和,加入一条线段就在对应的块中查询。
接下来考虑散点怎么处理,每次我们要处理一个散点在编号为 \([l, r]\) 的线段中被覆盖的次数,现在对于线段建主席树,把 \([l, r]\) 拆成主席树上的两个点,相减计算贡献。
那么在主席树上要做 \(O(n \sqrt n)\) 次操作,时间复杂度 \(O(n \sqrt n \log n)\),然后调整块长可以做到 \(O(n \sqrt{n \log n})\),这是官方题解做法。
现在开始优化主席树部分,我们发现修改有只有 \(n\) 次,而查询有 \(n \sqrt n\) 次,使用主席树做修改是非常浪费的,考虑用可持久化值域分块平衡两部分复杂度。
分块本质上也可以看作一个树形结构,由于要可持久化,所以把分块树建出来,每一个块用一个关键点表示,每一个散点用一个叶子表示。
那么每次修改只会影响 \(O(\sqrt n)\) 个关键点,以及散块的 \(O(\sqrt n)\) 个叶子,对于散块我们暴力复制所有点重构一遍,所有代表整块的关键点我们也复制一份,但是只在这些点上打标记,不递归到它下面的叶子。

容易发现单词修改只会影响 \(O(\sqrt n)\) 个点,从而时空复杂度为 \(O(n \sqrt n)\)。
Code
点击查看代码
#include <iostream>
#include <vector>
#pragma GCC optimize("Ofast")
using namespace std;
using LL = long long;
const int N = 1e5 + 5;
const int kL = 233, M = N / kL + 2;
const int tL = N * kL;
int n, q, now;
int fa[N], dfn[N], siz[N], rt[N];
int bl[N], cnt;
int L[M], R[M], f[N][M], c[M][N];
vector<int> e[N];
void dfs (int u) {
dfn[u] = ++now;
siz[u] = 1;
for (auto v : e[u]) {
if (v != fa[u]) {
dfs(v);
siz[u] += siz[v];
}
}
}
struct Sqrt_tree {
int totr, tots, totl, toti;
int son[N][M + 2];
int lson[N * 3][kL + 2];
int val[tL * 2], tag[tL], id[tL];
void builds (int &k, int l, int r) {
k = ++tots, id[k] = ++toti;
for (int i = l; i <= r; ++i) {
lson[id[k]][i - l + 1] = ++totl;
}
}
void build (int &k) {
k = ++totr;
for (int i = 1; i <= cnt; ++i) {
builds(son[k][i], L[i], R[i]);
}
}
void adds (int t, int &k, int l, int r, int L, int R) {
id[k = ++tots] = ++toti, tag[k] = tag[t];
copy(lson[id[t]] + 1, lson[id[t]] + R - L + 2, lson[id[k]] + 1);
for (int i = l; i <= r; ++i) {
int p = ++totl;
val[p] = val[lson[id[t]][i - L + 1]] + 1;
lson[id[k]][i - L + 1] = p;
}
}
void addall (int t, int &k) {
id[k = ++tots] = id[t], tag[k] = tag[t] + 1;
}
void add (int t, int &k, int l, int r) {
k = ++totr;
int bl = ::bl[l], br = ::bl[r];
copy(son[t] + 1, son[t] + cnt + 1, son[k] + 1);
if (bl == br) {
adds(son[t][bl], son[k][bl], l, r, L[bl], R[bl]);
}
else {
adds(son[t][bl], son[k][bl], l, R[bl], L[bl], R[bl]);
adds(son[t][br], son[k][br], L[br], r, L[br], R[br]);
for (int i = bl + 1; i < br; ++i) {
addall(son[t][i], son[k][i]);
}
}
}
int count (int k, int x) {
int s = son[k][bl[x]];
return val[lson[id[s]][x - L[bl[x]] + 1]] + tag[s];
}
} t;
LL query (int x, int l, int r) {
int bl = ::bl[l], br = ::bl[r];
auto qry = [&](int i) -> int {
return t.count(rt[x], dfn[i]);
};
LL res = 0;
if (bl == br) {
for (int i = l; i <= r; ++i) {
res += qry(i);
}
}
else {
for (int i = l; i <= R[bl]; ++i) {
res += qry(i);
}
for (int i = bl + 1; i < br; ++i) {
res += f[x][i];
}
for (int i = L[br]; i <= r; ++i) {
res += qry(i);
}
}
return res;
}
int main () {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 2; i <= n; ++i) {
cin >> fa[i];
e[fa[i]].push_back(i);
}
dfs(1);
for (int i = 1; i <= n; ++i) {
bl[i] = (i - 1) / kL + 1;
}
cnt = bl[n];
for (int i = 1; i <= cnt; ++i) {
L[i] = R[i - 1] + 1;
R[i] = min(L[i] + kL - 1, n);
}
for (int i = 1; i <= cnt; ++i) {
for (int j = L[i]; j <= R[i]; ++j) {
++c[i][dfn[j]];
}
for (int j = 1; j <= n; ++j) {
c[i][j] += c[i][j - 1];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= cnt; ++j) {
f[i][j] = f[i - 1][j] + c[j][dfn[i] + siz[i] - 1] - c[j][dfn[i] - 1];
}
}
t.build(rt[0]);
for (int i = 1; i <= n; ++i) {
t.add(rt[i - 1], rt[i], dfn[i], dfn[i] + siz[i] - 1);
}
cin >> q;
for (LL la = 0, l, r; q--; ) {
cin >> l >> r;
l ^= la, r ^= la;
l = l % n + 1, r = r % n + 1;
if (l > r) swap(l, r);
cout << (la = (query(r, l, r) - query(l - 1, l, r))) << '\n';
}
return 0;
}
【ZROJ2730】简单题 可持久化分块题解的更多相关文章
- 题解[LuoguP6222]「P6156简单题」加强版
题解[LuoguP6222]「P6156简单题」加强版 加强版很好地体现了这个题的真正价值.(当然是指卡常 本题解给出了本题更详尽的推倒导和思考过程,思路与 CYJian 的类似,具体式子的个别地方换 ...
- yhm的丘赛题解(其中的一些简单题)
有选择地做了丘赛里的一些简单题,不定期更新 目录 [简单组合数学]2011丘赛个人[应数计算数学概统]第3题题解 [拉格朗日多项式插值]2011丘赛个人[应数计算数学概统]第2题题解 [概率] ...
- 洛谷 P5057 [CQOI2006]简单题 题解
P5057 [CQOI2006]简单题 题目描述 有一个 n 个元素的数组,每个元素初始均为 0.有 m 条指令,要么让其中一段连续序列数字反转--0 变 1,1 变 0(操作 1),要么询问某个元素 ...
- hectf2020部分简单题题解wp
HECTF 我真是又菜又没时间肝题..又又又只水了波简单题... Reverse 1.Hello_Re file查一波 32bit,拖进IDA中 老规矩shift+F12 查看字符串: 跳转 F5查看 ...
- unctf2020 部分简单题题解
unctf2020 水一波简单题..菜狗前来报道..大佬轻喷..如果有时间做题就好了呜呜呜 misc 1.baba_is_you 题目告诉我们,了解png文件格式. 下载得到一张png格式的图片. 用 ...
- [BZOJ2683][BZOJ4066]简单题
[BZOJ2683][BZOJ4066]简单题 试题描述 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x ...
- 又一道简单题&&Ladygod(两道思维水题)
Ladygod Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit S ...
- noip做题记录+挑战一句话题解?
因为灵巧实在太弱辽不得不做点noip续下命QQAQQQ 2018 积木大赛/铺设道路 傻逼原题? 然后傻逼的我居然检查了半天是不是有陷阱最后花了差不多一个小时才做掉我做过的原题...真的傻逼了我:( ...
- 【BZOJ1176】[Balkan2007]Mokia/【BZOJ2683】简单题 cdq分治
[BZOJ1176][Balkan2007]Mokia Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=1600 ...
- 【LG4148】简单题
[LG4148]简单题 题面 洛谷 题解 \(kdt\)模板题呀... #include <iostream> #include <cstdio> #include <c ...
随机推荐
- 《Python数据可视化之matplotlib实践》 源码 第一篇 入门 第四章
图 4.1 import matplotlib import matplotlib.pyplot as plt import numpy as np # 设置matplotlib正常显示中文和负号 m ...
- 由于美国的制程限制,假如我国的同等性能的AI芯片5年内无法实现量产化我们应该如何发展我们的AI领域的基础设施呢?
相关: 美晶片禁令面難題!封過頭反把市場送中國? 今年华为公司推出了mate pro60手机,可以说我们可以实现7nm芯片的制造了,但是要注意,我们在实现7nm芯片制造的时候使用的应该依旧是被美国限制 ...
- 利用强化学习算法解释人类脑对高维状态的抽象表示:how humans can map high-dimensional sensory inputs in actions
论文: <Using deep reinforcement learning to reveal how the brain encodes abstract state-space repre ...
- 如何修复ubuntu的uefi启动——如何将Ubuntu安装入移动硬盘中
交代一下使用场景,个人平时经常使用Ubuntu系统,由于不喜欢总在一个地方呆但是来回搬电脑又不是十分的方便,于是想到了一个好的方案,那就是把Ubuntu系统安装到移动硬盘中,这样不论是在家还是在实验室 ...
- 新版的Django Docker部署方案,多阶段构建、自动处理前端依赖
前言 前几天的文章中,我们已经把使用 pdm 的项目用 docker 搞定了,那么下一步就是把完整的 DjangoStarter v3 版本用 docker 部署. 现在不像之前那么简单直接一把梭了, ...
- 什么是API?(详解)
编程资料时经常会看到API这个名词,网上各种高大上的解释估计放倒了一批初学者.初学者看到下面这一段话可能就有点头痛了. API(Application Programming Interface,应用 ...
- USB入门系列(一)认识USB
认识USB usb的类型 接头外形上 USB类型 描述 USB-A 最广泛的接口标准 USB-B 一般用于打印机.扫描仪.USBHUB等外部USB设备(j-tag就用到了) USB-C USB-C将成 ...
- 编译器实现之旅——第十六章 代码装载、链接器、全局变量与main函数
在上一章的旅程中,我们已经实现了函数调用的代码生成器分派函数,但在上一章的末尾,我们留下了三个问题: 我们需要为全局变量压栈 main函数需要在程序启动时被自动调用 我们需要实现一个链接器,以将所有的 ...
- 调用ArrayList的add方法抛异常UnsupportedOperationException
调用ArrayList的add方法抛异常UnsupportedOperationException 对于一些想要把数组转成List的需求,可能会使用到Arrays.asList()获取List对象,但 ...
- 安装Kubernetes(k8s)保姆级教程---无坑版
一.安装环境说明 硬件要求 内存:2GB或更多RAM CPU: 2核CPU或更多CPU 硬盘: 30GB或更多 本次环境说明: 操作系统:CentOS 7.9 内核版本:3.10.0-1160 mas ...