【P4211 LNOI2014】LCA——树链剖分 +询问离线
(7.16晚)更完先在B站颓一会儿……
---------------------------------------------------------------
(以下为luogu题面)
题目描述
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。 有q次询问,每次询问给出l r z,求∑(l≤i≤r dep[LCA(i,z)])
输入输出格式
输入格式:
第一行2个整数n q。 接下来n-1行,分别表示点1到点n-1的父节点编号。 接下来q行,每行3个整数l r z。
输出格式:
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
说明
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
需要说明的是,这道题的思路跟LCA的三种求法无关(树剖太暴力了,而且确实会用到树剖),也就是说没有丝毫的暴力分可拿。(掀桌)
从这道题中对于深度的定义可以发现,两个点LCA的深度等价于它们的公共祖先的数目。那么问题的本质是,每次给定一个点集,求集合中每个点与一个定点的祖先数目的累计。考虑这样的暴力处理:对于任取一个点u与所给定点z,我们可以先给u的每个祖先都打一层标记,然后询问从z到根有标记的点的数目。这个过程显然可以用树剖处理,我们在树剖序上架一个线段树支持区间修改、区间查询,每个这样的询问可以做到log^2n的复杂度。
如果我们对每次询问,都把点集中的每个点这样打一遍祖先的标记再查询z,总复杂度会多出一个q来,显然需要进一步优化。题中给出的点集有一个非常好的性质:点集中的点处于一段连续区间中。那么,我们可以把每个询问[l, r]差分为[1, l - 1]和[1, r]两个询问,那么需要维护的就只有形如[1, R]与每个z的关系了。
因此我们把询问离线差分后按右端点排序,从左向右扫描点集[1, n]并打标记,每遇到一个询问则查询给定z与根之间的标记数目,统计进询问数组即可。
代码:
- #include <cstdio>
- #include <iostream>
- #include <cstring>
- #include <algorithm>
- #define lowbit(i) (i & -i)
- #define maxn 50010
- #define mod 201314
- template <typename T>
- void read(T &x) {
- x = 0;
- int f = 1;
- char ch = getchar();
- while (!isdigit(ch)) {
- if (ch == '-')
- f = -1;
- ch = getchar();
- }
- while (isdigit(ch)) {
- x = x * 10 + (ch ^ 48);
- ch = getchar();
- }
- x *= f;
- return;
- }
- using namespace std;
- int head[maxn], top;
- struct E {
- int to, nxt;
- } edge[maxn];
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- int n, q, ans[maxn], qtp;
- struct Q {
- int r, z, id;
- bool op;
- friend bool operator < (Q a, Q b) {
- return a.r < b.r;
- }
- } ask[maxn << 1];
- /*namespace BIT {
- int bit[maxn];
- void modify(int x, int val) {
- for (int i = x; i <= n; i += lowbit(i))
- bit[i] += val;
- }
- int presum(int x) {
- int sum = 0;
- for (int i = x; i; i -= lowbit(i))
- sum = (sum + bit[i]) % mod;
- return sum;
- }
- }*/
- namespace Segment_tree {
- #define lc (nd<<1)
- #define rc ((nd<<1)|1)
- #define mid ((l+r)>>1)
- struct node {
- int val, len;
- friend node operator + (node a, node b) {
- return (node) {(a.val + b.val) % mod, a.len + b.len};
- }
- } seg[maxn << 2];
- int tag[maxn << 2];
- inline void update(int nd) {
- seg[nd] = seg[lc] + seg[rc];
- }
- inline void put_tag(int nd, int op) {
- seg[nd].val += op * seg[nd].len;
- tag[nd] += op;
- }
- inline void push_down(int nd) {
- put_tag(lc, tag[nd]);
- put_tag(rc, tag[nd]);
- tag[nd] = 0;
- }
- void build(int nd, int l, int r) {
- if (l == r) {
- seg[nd] = (node) {0, 1};
- return;
- }
- build(lc, l, mid);
- build(rc, mid + 1, r);
- update(nd);
- }
- void modify(int nd, int l, int r, int ql, int qr, int val) {
- if (l >= ql && r <= qr) {
- put_tag(nd, val);
- return;
- } else if (l > qr || r < ql)
- return;
- push_down(nd);
- modify(lc, l, mid, ql, qr, val);
- modify(rc, mid + 1, r, ql, qr, val);
- update(nd);
- return;
- }
- int query(int nd, int l, int r, int ql, int qr) {
- if (l >= ql && r <= qr)
- return seg[nd].val;
- if (l > qr || r < ql)
- return 0;
- push_down(nd);
- return (query(lc, l, mid, ql, qr) + query(rc, mid + 1, r, ql, qr)) % mod;
- }
- }
- namespace Div_tree { //树剖
- // using namespace BIT; //试图用树状数组维护区间修改的惨痛失败
- using namespace Segment_tree;
- int dfn[maxn], size[maxn], ftop[maxn], d[maxn], son[maxn], f[maxn];
- int tmr;
- void dfs1(int u, int pre) {
- f[u] = pre;
- d[u] = d[pre] + 1;
- size[u] = 1;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- dfs1(v, u);
- size[u] += size[v];
- if (size[v] > size[son[u]])
- son[u] = v;
- }
- }
- void dfs2(int u, int tp) {
- dfn[u] = ++tmr;
- ftop[u] = tp;
- if (!son[u])
- return;
- dfs2(son[u], tp);
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v != son[u])
- dfs2(v, v);
- }
- }
- void Mrange(int u, int v, int val) {
- while (ftop[u] != ftop[v]) {
- if (d[ftop[u]] < d[ftop[v]])
- swap(u, v);
- modify(1, 1, n, dfn[ftop[u]], dfn[u], val);
- u = f[ftop[u]];
- }
- if (d[u] < d[v]) swap(u, v);
- modify(1, 1, n, dfn[v], dfn[u], val);
- return;
- }
- int Qrange(int u, int v) {
- int sum = 0;
- while (ftop[u] != ftop[v]) {
- if (d[ftop[u]] < d[ftop[v]])
- swap(u, v);
- sum = (sum + query(1, 1, n, dfn[ftop[u]], dfn[u])) % mod;
- u = f[ftop[u]];
- }
- if (d[u] < d[v]) swap(u, v);
- sum += query(1, 1, n, dfn[v], dfn[u]);
- return sum % mod;
- }
- } using namespace Div_tree;
- void init() {
- build(1, 1, n);
- dfs1(1, 0);
- dfs2(1, 1);
- }
- int main() {
- read(n), read(q);
- int u, v, z;
- for (int i = 2; i <= n; ++i)//编号+1
- read(u), ++u, insert(u, i);
- for (int i = 1; i <= q; ++i) {
- read(u), read(v), read(z);
- ask[++qtp] = (Q) {u, z+1, i, 0}; //拆询问
- ask[++qtp] = (Q) {v+1, z+1, i, 1};
- }
- sort(ask + 1, ask + qtp + 1);
- init();
- int i = 0, j = 1;
- while (j <= qtp) {
- while (i < ask[j].r)
- Mrange(1, ++i, 1);
- if (ask[j].op)
- ans[ask[j].id] += Qrange(1, ask[j].z); //按标记统计进答案
- else ans[ask[j].id] -= Qrange(1, ask[j].z);
- ++j;
- }
- for (int i = 1; i <= q; ++i)
- printf("%d\n", (ans[i]+mod)%mod);
- return 0;
- }
【P4211 LNOI2014】LCA——树链剖分 +询问离线的更多相关文章
- 洛谷 P4211 [LNOI2014]LCA (树链剖分+离线)
题目:https://www.luogu.org/problemnew/solution/P4211 相当难的一道题,其思想难以用言语表达透彻. 对于每个查询,区间[L,R]中的每个点与z的lca肯定 ...
- 洛谷$P4211\ [LNOI2014]\ LCA$ 树链剖分+线段树
正解:树剖+线段树 解题报告: 传送门$QwQ$ 看到$dep[lca]$啥的就想到之前托腮腮$CSP$模拟$D1T3$的那个套路,,, 然后试下这个想法,于是$dep[lca(x,y)]=\sum_ ...
- [BZOJ3626] [LNOI2014]LCA(树链剖分)
[BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2050 Solved: 817[Submit][Status ...
- BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )
说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...
- BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线
http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题 ...
- BZOJ3626[LNOI2014]LCA——树链剖分+线段树
题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...
- bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)
Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...
- 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树
题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...
随机推荐
- vue-cli axios ie9 问题
vue在ie9中碰到的问题 最近我们的项目选择用vue来做开发,在这个过程我们还要兼容ie9这个坑,在这里我写一点我碰到的坑 开发选用:vue+vue-cli+axios+router+iview+m ...
- Mybatis---01Mybatis动态代理过程分析
1.通过调试,session调用的getMapper是其实现类DefaultSQLSession中的 //1.读取配置文件 InputStream in = Resources.getResource ...
- 10 Servlet_02 资源跳转(主要是内部转发)与中文乱码问题
总的知识点: 1.小的知识点总结: alt + shift + r 重命名快捷键(可以给包和类以及项目重命名) 有序列表 ol li 无序列表 ul type 格式 text 是文本类型 passwo ...
- 嵌入式以太网模块的TCP Client模式说明
嵌入式以太网模块采用TTL电平串口,支持TCP Server,TCP Client,UDP Slave,UDP Master,TCP-ZSD,UDP-ZSD多种通信协议,TCP服务器模式支持多连接,可 ...
- APIview + Serializers
1.APIview使用 https://www.cnblogs.com/xiaonq/p/10124104.html https://www.cnblogs.com/xiaonq/p/109878 ...
- 谈谈OKHttp的几道面试题
来吧,今天说说常用的网络框架OKHttp,也是现在Android所用的原生网络框架(Android 4.4开始,HttpURLConnection的底层实现被Google改成了OkHttp),GOGO ...
- 推动中国制造升级,汽车装配车间生产流水线3D可视化
前言 随着<中国制造2025>的提出,制造业迎来了全新的发展机遇.更多的企业将制造业信息化技术进行广泛的应用,如 MES 系统.数字孪生以及生产管理可视化等技术的研究应用,已经成为社会各界 ...
- 知识补充之Django缓存
缓存 简单概括就是将对数据库操作查询所得到的数据放入另外一台机器上(缓存)中,当用户再次请求时,直接去缓存中拿,避免对数据库的频繁操作,加快数据的显示时间,需要知道的是,缓存里面的数据一般都设置有超时 ...
- 知识全聚集 .Net Core 技术突破 | 简单说说工作单元
知识全聚集 .Net Core 技术突破 | 简单说说工作单元 教程 01 | 模块化方案一 02 | 模块化方案二 其他教程预览 分库分表项目实战教程 Git地址: https://github.c ...
- Table is marked as crashed and should be repaired 解决办法
遇到这个问题几个敲命令轻松搞定 1.首先进入mysql命令台: mysql -u root -p 回车 输入密码 2.查询所有的库 mysql> show databases; 3.进入数据库 ...