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. 【C】Re09 结构体

    一.结构体 Struct 创建和基本使用 #include <stdio.h> #include <stdlib.h> #include <string.h> // ...

  2. MindSpore分布式并行训练 (GPU-Docker)mindspore—1.2.1—gpu—docker版本运行报错,Failed to init nccl communicator for group,init nccl communicator for group nccl_world_group

    如题目所述: 计算框架MindSpore分布式并行训练报错,具体版本:docker-gpu-1.2.1 运行环境: 硬件:Intel CPU, 4卡泰坦 软件:Ubuntu18.04宿主机,docke ...

  3. 软件开发工程师,几款常用的APP,你用过几款?最后一个测试网络必备

    作为一名程序员,手机里一定有几个常用的app,下面给大家推荐几款. 1. CSDN 国内最大编程论坛:虽然有多少人吐槽现在使用csdn就像屎里淘金, 但是不得不承认他仍然是大家搜索技术资料.问题的首选 ...

  4. Camera | 1.Camera基础知识

    一口君最近在玩瑞芯微的板子,之前写了几篇基于瑞芯微的文章,大家可以学习一下. <瑞芯微rk356x板子快速上手> <Linux驱动|rtc-hym8563移植笔记> <L ...

  5. 手把手教Linux驱动5-自旋锁、信号量、互斥体概述

    在Linux系统中有大量的临界资源需要保护,如何让各个任务有条不紊的访问这些资源,这涉及到Linux中并发访问的保护机制设计相关知识.后面会详细介绍这几个机制. (据可靠消息,锁的实现经常出现在笔试环 ...

  6. python中怎样指定open编码为ansi

    在Python中,当使用open函数打开文件时,可以通过encoding参数来指定文件的编码方式.然而,需要注意的是,Python标准库中的编码并不直接支持名为"ANSI"的编码, ...

  7. 使用 refreshNuxtData 刷新 Nuxt应用 中的数据

    title: 使用 refreshNuxtData 刷新 Nuxt应用 中的数据 date: 2024/8/21 updated: 2024/8/21 author: cmdragon excerpt ...

  8. portainer安装&升级

    2024年4月15日 关于升级: 如果需要升级 Portainer,请按以下步骤操作: 使用以下命令列出所有镜像: docker ps -a 根据需要删除指定镜像: docker rm <镜像名 ...

  9. 微信小程序中使用Echarts展示折线图

    效果图 主要实现的功能输入地区和频次查询油价的调整消息 1.从echarts-for-weixin官网下载文件 2.项目中引入echarts 将整个文件夹放在项目pages同级的目录下面 import ...

  10. 宝塔安装onlyoffice

    1. 拉取镜像 docker pull onlyoffice/documentserver 2. 构建容器 进入宝塔管理面板->docker->镜像,可以看到镜像已被安装成功 配置容器参数 ...