[BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】
题目链接: BZOJ - 3626
题目分析
考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和。
这个方法是可以叠加的,这是非常有用的一点。如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和。
不仅满足可加性,还满足可减性,这就更好了!
那么我们就可以对每个询问 [l, r] 做一个差分,用 Query(r) - Query(l - 1) 作为答案。这样就有一种离线算法:将 n 个点依次操作,将其到 Root 的路径上的点权值 +1 ,然后如果这个点是某个询问的 l - 1 或 r ,就用那个询问的 c 求一下到 Root 的路径和,算入答案中。
Done!
写代码的时候忘记 % Mod 真是弱...
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector> using namespace std; const int MaxN = 50000 + 5, Mod = 201314; int n, m, Index;
int Father[MaxN], Depth[MaxN], Size[MaxN], Son[MaxN], Top[MaxN], Pos[MaxN];
int T[MaxN * 4], D[MaxN * 4], Len[MaxN * 4], Ans[MaxN], Q[MaxN]; vector<int> BA[MaxN], EA[MaxN]; struct Edge
{
int v;
Edge *Next;
} E[MaxN], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y) {
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} int DFS_1(int x, int Dep) {
Depth[x] = Dep;
Size[x] = 1;
int SonSize, MaxSonSize;
SonSize = MaxSonSize = 1;
for (Edge *j = Point[x]; j; j = j -> Next) {
SonSize = DFS_1(j -> v, Dep + 1);
if (SonSize > MaxSonSize) {
MaxSonSize = SonSize;
Son[x] = j -> v;
}
Size[x] += SonSize;
}
return Size[x];
} void DFS_2(int x) {
if (x == Son[Father[x]]) Top[x] = Top[Father[x]];
else Top[x] = x;
Pos[x] = ++Index;
if (Son[x] != 0) DFS_2(Son[x]);
for (Edge *j = Point[x]; j; j = j -> Next)
if (j -> v != Son[x]) DFS_2(j -> v);
} void Build_Tree(int x, int s, int t) {
Len[x] = t - s + 1;
D[x] = T[x] = 0;
if (s == t) return;
int m = (s + t) >> 1;
Build_Tree(x << 1, s, m);
Build_Tree(x << 1 | 1, m + 1, t);
} inline void Update(int x) {
T[x] = T[x << 1] + T[x << 1 | 1];
T[x] %= Mod;
} inline void Paint(int x, int Num) {
T[x] += Num * Len[x];
T[x] %= Mod;
D[x] += Num;
D[x] %= Mod;
} inline void PushDown(int x) {
if (D[x] == 0) return;
Paint(x << 1, D[x]);
Paint(x << 1 | 1, D[x]);
D[x] = 0;
} void Add(int x, int s, int t, int l, int r) {
if (l <= s && r >= t) {
Paint(x, 1);
return;
}
PushDown(x);
int m = (s + t) >> 1;
if (l <= m) Add(x << 1, s, m, l, r);
if (r >= m + 1) Add(x << 1 | 1, m + 1, t, l, r);
Update(x);
} void EAdd(int x) {
int fx;
fx = Top[x];
while (fx != 1) {
Add(1, 1, n, Pos[fx], Pos[x]);
x = Father[fx];
fx = Top[x];
}
Add(1, 1, n, Pos[1], Pos[x]);
} int Get(int x, int s, int t, int l, int r) {
if (l <= s && r >= t) return T[x];
int ret = 0;
PushDown(x);
int m = (s + t) >> 1;
if (l <= m) ret += Get(x << 1, s, m, l, r);
if (r >= m + 1) ret += Get(x << 1 | 1, m + 1, t, l, r);
return ret % Mod;
} int EGet(int x) {
int ret = 0, fx;
fx = Top[x];
while (fx != 1) {
ret += Get(1, 1, n, Pos[fx], Pos[x]);
ret %= Mod;
x = Father[fx];
fx = Top[x];
}
ret += Get(1, 1, n, Pos[1], Pos[x]);
return ret % Mod;
} int main()
{
scanf("%d%d", &n, &m);
int a, b, c;
for (int i = 2; i <= n; ++i) {
scanf("%d", &a);
++a;
Father[i] = a;
AddEdge(a, i);
}
DFS_1(1, 1);
Index = 0;
DFS_2(1);
Build_Tree(1, 1, n);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &a, &b, &c);
++a; ++b; ++c;
Q[i] = c;
BA[a - 1].push_back(i);
EA[b].push_back(i);
}
for (int i = 1; i <= n; ++i) {
EAdd(i);
for (int j = 0; j < BA[i].size(); ++j)
Ans[BA[i][j]] -= EGet(Q[BA[i][j]]);
for (int j = 0; j < EA[i].size(); ++j)
Ans[EA[i][j]] += EGet(Q[EA[i][j]]);
}
for (int i = 1; i <= m; ++i) printf("%d\n", (Ans[i] + Mod) % Mod);
return 0;
}
[BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】的更多相关文章
- 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那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题 ...
- bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)
Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...
- BZOJ 3626 [LNOI2014]LCA ——树链剖分
思路转化很巧妙. 首先把询问做差分. 然后发现加入一个点就把路径上的点都+1,询问的时候直接询问到根的路径和. 这样和原问题是等价的,然后树链剖分+线段树就可以做了. #include <map ...
- bzoj3626: [LNOI2014]LCA (树链剖分+离线线段树)
Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. ...
- 洛谷 P4211 [LNOI2014]LCA (树链剖分+离线)
题目:https://www.luogu.org/problemnew/solution/P4211 相当难的一道题,其思想难以用言语表达透彻. 对于每个查询,区间[L,R]中的每个点与z的lca肯定 ...
- [LNOI2014]LCA 树链剖分 离线 前缀和 思维题
题目描述:给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每 ...
- [BZOJ3626] [LNOI2014]LCA(树链剖分)
[BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...
随机推荐
- 手把手教学:详解HTML5移动开发框架PhoneJS
摘要:HTML/JavaScript的优势自不必说,但却也并非完美,相比之下,原生App占内存更少.响应更快.本文详解了HTML5移动开发框架PhoneJS的使用全过程,通过它,能够让Web应用在移动 ...
- winform设置DataGridView样式 (蓝色)
本文转载:http://www.cnblogs.com/hailexuexi/archive/2012/04/23/2466398.html 代码: #region DataGridVeiw Styl ...
- [Flux] 3. Actions
Actions contain no functionality, but rather describe an event in our application. In this lesson we ...
- linux 命令c语言代码实现
自己学习<APUE>时写的linux下一些命令(大概40个左右)实现,仅当学习使用,这些命令包含cat cp echo head ls paste rmdir tail umask who ...
- Web classPath
classpath,看名字,类路径,这样比如,对于java程序,就是告诉java程序哪里去找类.(java虚拟机都是通过类装载器的)想myeclipse中struts,spring,hibernate ...
- iOS--RunLoop原理介绍
什么是RunLoop RunLoop从字面上看是运行循环的意思,这一点也不错,它确实就是一个循环的概念,或者准确的说是线程中的循环. 本文一开始就提到有些程序是一个圈,这个圈本质上就是这里的所谓的Ru ...
- 禁止鼠标多次点击选中div中的文字
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Fire ...
- tj
--统计set @collSql='select sum(case Ca_IssueType when 0 then 1 else 0 end) as IssueCount,sum(case when ...
- C#获取本机IP搜集整理7种方法
今天打算试着写个小聊天程序,但是要用到获取本机IP,以前从没用过.摆渡百度了一会儿,出于贪心,想把各种获取本机IP的方法给找出来.摆渡+测试了几个小时,于是有了下面的成果,有点小累,但看到这些成果,也 ...
- javascript基础学习(九)
javascript之基本包装类型 学习要点: 基本包装类型概述 Boolean类型 Number类型 String类型 一.基本包装类型概述 为了便于操作基本类型值,提供了3种特殊的引用类型:Boo ...