【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次询问,每次询 ...
随机推荐
- python接口自动化测试遇到的问题及解决方案
工作中xml中的某一个字段是全网唯一,这就需要进行参数化处理.此次对这一个字段进行参数化处理引用了random模块和index()函数.代码如下: #!/usr/bin/python # -*- co ...
- 看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了
我把自己以往的文章汇总成为了 Github ,欢迎各位大佬 star https://github.com/crisxuan/bestJavaer 已提交此篇文章 final 是 Java 中的关键字 ...
- Java学习的第四十八天
1.例8.4找出整形数组中的最大值 import java.util.Scanner; public class Cjava { public static void main(String[]arg ...
- 初始化vue项目
1.创建vue项目命令 vue init webpack deaxios # 使用脚手架创建项目 deaxios(项目名,随便取得) cd deaxios # 进入项目 npm install axi ...
- shell脚本之文件测试表达式
1.文件测试表达式的用法 我们在编程时处理一个对象时,需要对对象进行测试,只有符合要求的才采取操作处理:这样做的好处是避免程序出错以及无所畏的消耗系统资源,这个测试的对象可以是文件.字符串.数字等. ...
- Windows10系统下Hadoop和Hive开发环境搭建填坑指南
前提 笔者目前需要搭建数据平台,发现了Windows系统下,Hadoop和Hive等组件的安装和运行存在大量的坑,而本着有坑必填的目标,笔者还是花了几个晚上的下班时候在多个互联网参考资料的帮助下完成了 ...
- python机器学习实现人脸图片自动补全
人脸自动补全 关注公众号"轻松学编程"了解更多. 1.导包 import matplotlib.pyplot as plt import numpy as np import pa ...
- 什么是 session 和 cookie
cookie 大家应该都熟悉,比如说登录某些网站一段时间后,就要求你重新登录:再比如有的同学很喜欢玩爬虫技术,有时候网站就是可以拦截住你的爬虫,这些都和 cookie 有关.如果你明白了服务器后端对于 ...
- 什么是4G模块 4G模块的工作原理及特点
什么是4G模块 4G模块,也被叫做4G通信模块或4G DTU模块,他是物联网行业具有4G通信功能的一种产品,通过4G模块,我们可以实现工业设备数据通过无线4G网络传输到远端控制中心,并从控制中心通过4 ...
- 论文解读 - MaskGAN:BETTER TEXT GENERATION VIA FILLING IN THE _____
1 简介 文本生成是自然语言处理中一个重要的研究领域,具有广阔的应用前景.当前主流的用来进行文本生成的模型主要是Seq2Seq模型,通常利用maximum likelihood和teacher for ...