【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 ...
随机推荐
- 【Shiro】01 概述 & 快速上手
什么是Shiro? Apache Shiro 是Java的一个权限安全框架 一些功能:认证.授权.加密.会话管理.与Web 集成.缓存等 Shiro官网地址:[ 点击访问 ] http://shi ...
- 代码随想录Day3
203.移除链表元素 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 . 示例 1: 输入:head = [1 ...
- MIT6.1810の学习笔记
webliuのmit.6.828学习笔记 写在前面 本文基于mit/6.828课程,附官方网址. 本文采用的实验环境为2020年版的xv6系统,需要wsl,vscode,docker工具.附环境配置教 ...
- nvidia公司官方迁移学习套件 —— NVIDIA TAO Toolkit
资料: https://blogs.nvidia.com/blog/what-is-transfer-learning/ 相关: https://developer.nvidia.com/tao-to ...
- 【转载】 arXiv论文提交流程
原文地址: https://blog.csdn.net/u010705932/article/details/105834469 =================================== ...
- 关于spinninup的学习笔记
作为reinforcement learning的一个入门学习的项目,spinningup的地址: (英文原版:) https://spinningup.openai.com/en/latest/in ...
- mongo变更流使用及windows下副本集五分钟搭建
mongodb的变更流解释: 变更流(Change Streams)允许应用程序访问实时数据变更,从而避免事先手动追踪 oplog 的复杂性和风险.应用程序可使用变更流来订阅针对单个集合.数据库或整 ...
- SMU Spring 2023 Trial Contest Round 11
A. The Text Splitting 题意:给出字符串长度,给出p和q两种切割方式,任选其中一种,把字符串分割输出结果. 题解:先进行判断,p和q是否能整个的分割n,利用p和q的函数关系判断(见 ...
- 2023 ICPC 香港
gym 开场发现 E 是传统数据结构题很高兴,不过先跳了.F 知道相邻两段的长度差 \(\le1\),以为最终每段长度只有 \(\lfloor\frac{n}{m+1}\rfloor,\lceil\f ...
- Devexpress 控件学习记录(一:BarManager 控件、XtraTabbedMdiManager 控件)
BarManager 控件 最终实现的效果如下: 首先在窗体中拖出BarManager控件,窗体Bar add地方点击添加 设置BarManager的属性 设置出现的窗体的底部[DockStyle=B ...