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】简单题 可持久化分块题解的更多相关文章

  1. 题解[LuoguP6222]「P6156简单题」加强版

    题解[LuoguP6222]「P6156简单题」加强版 加强版很好地体现了这个题的真正价值.(当然是指卡常 本题解给出了本题更详尽的推倒导和思考过程,思路与 CYJian 的类似,具体式子的个别地方换 ...

  2. yhm的丘赛题解(其中的一些简单题)

    有选择地做了丘赛里的一些简单题,不定期更新     目录 [简单组合数学]2011丘赛个人[应数计算数学概统]第3题题解 [拉格朗日多项式插值]2011丘赛个人[应数计算数学概统]第2题题解 [概率] ...

  3. 洛谷 P5057 [CQOI2006]简单题 题解

    P5057 [CQOI2006]简单题 题目描述 有一个 n 个元素的数组,每个元素初始均为 0.有 m 条指令,要么让其中一段连续序列数字反转--0 变 1,1 变 0(操作 1),要么询问某个元素 ...

  4. hectf2020部分简单题题解wp

    HECTF 我真是又菜又没时间肝题..又又又只水了波简单题... Reverse 1.Hello_Re file查一波 32bit,拖进IDA中 老规矩shift+F12 查看字符串: 跳转 F5查看 ...

  5. unctf2020 部分简单题题解

    unctf2020 水一波简单题..菜狗前来报道..大佬轻喷..如果有时间做题就好了呜呜呜 misc 1.baba_is_you 题目告诉我们,了解png文件格式. 下载得到一张png格式的图片. 用 ...

  6. [BZOJ2683][BZOJ4066]简单题

    [BZOJ2683][BZOJ4066]简单题 试题描述 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x ...

  7. 又一道简单题&&Ladygod(两道思维水题)

    Ladygod Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit S ...

  8. noip做题记录+挑战一句话题解?

    因为灵巧实在太弱辽不得不做点noip续下命QQAQQQ 2018 积木大赛/铺设道路 傻逼原题? 然后傻逼的我居然检查了半天是不是有陷阱最后花了差不多一个小时才做掉我做过的原题...真的傻逼了我:( ...

  9. 【BZOJ1176】[Balkan2007]Mokia/【BZOJ2683】简单题 cdq分治

    [BZOJ1176][Balkan2007]Mokia Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=1600 ...

  10. 【LG4148】简单题

    [LG4148]简单题 题面 洛谷 题解 \(kdt\)模板题呀... #include <iostream> #include <cstdio> #include <c ...

随机推荐

  1. 【Java】找不到此类异常

    Java.lang.classNotFoundException 找不到此类异常: java.lang.ClassNotFoundException: org.springframework.web. ...

  2. 【Java-GUI】12 Swing07 JList

    列表和下拉选择: package cn.dzz; import javax.swing.*; import javax.swing.border.EtchedBorder; import javax. ...

  3. 网友开放的开源项目:网页版的A*算法可视化演示程序

    相关项目: https://xueqiaoxu.me/#projects 项目介绍: A JavaScript path-finding library for grid based games. I ...

  4. (待续)【转载】 Deep Reinforcement Learning Doesn't Work Yet(这里有一篇深度强化学习劝退文)

    原文: https://www.alexirpan.com/2018/02/14/rl-hard.html ============================================== ...

  5. Ubuntu22.04系统安装DeepMind Lab

    相关资料: DeepMind Lab的一些python例子-----(Ubuntu22.04系统安装DeepMind Lab)后续 ================================== ...

  6. 快手 内推码:TYORVzmsw 秋招 应届生/实习生 真正本人内推 已有多人在我内推之后,接连顺利通过了HR筛选、用人部门筛选、面试!

    内推码:TYORVzmsw 校园招聘岗位列表:https://campus.kuaishou.cn/#/campus/jobs?code=TYORVzmsw 真正的本人内部推荐! 已有多人在我内推之后 ...

  7. Linux 常见编辑器

    命令行编辑器 Vim Linux 上最出名的编辑器当属 Vim 了.Vim 由 Vi 发展而来,Vim 的名字意指 Vi IMproved,表示 Vi 的升级版.Vim 对于新手来说使用比较复杂,不过 ...

  8. Windows SSH 免密登陆远程计算机

    上传公钥 如果远程计算机是类 Unix 系统,使用下面这条命令: Get-Content $Env:USERPROFILE\.ssh\id_rsa.pub | ssh USER@HOST " ...

  9. Prometheus 告警恢复时,怎么获取恢复时的值?

    Prometheus 告警事件中的 $value 表示当前告警触发时的值,但是在告警恢复时,Resolved 事件中的 $value 仍然是最新告警时的值,并非是恢复时的值,这是什么原因和原理?是否有 ...

  10. ELK快速部署(踩坑记录、常见报错解决)及常用架构讲解

    ELK = Elasticserach + Logstash + kibana(包含但不仅限于) 简介: Elasticsearch:分布式搜索和分析引擎,具有高可伸缩.高可靠和易管理等特点.基于 A ...