【树链剖分 差分】bzoj3626: [LNOI2014]LCA
把LCA深度转化的那一步还是挺妙的。之后就是差分加大力数据结构了。
Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
Input
第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
Output
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
Sample Input
0
0
1
1
1 4 3
1 4 2
Sample Output
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
题目分析
我的做法&图片分析
由于树的标号是随便标的,所以这里的$[l,r]$区间操作在树上并没有什么大的特殊意义。
注意到如果$z$是给定的话,那么由于答案可减,我们可以对于序列来分块。
当然分块只是一种想法,再来挖掘一下$z$给定情况的性质。
这里求4和7的LCA。自然,LCA就是两点到根节点的第一个公共节点。不过这幅图可能还没看出什么。
这幅图里7号节点是z,4,6,8节点是与z节点查询的节点。我们把与z节点查询的节点都沿路径向根方向+1。
这样只需要查询z节点到根节点的路径权值和就能算出所有LCA的深度之和了。
注意到这样处理之后,预处理的信息对于任意的z都适用了。
至于题目要求的“根节点深度为1”,正好可以让我们把边权化为点权,做起来也更加方便。
更理论化的题解
在黄学长博客上看到一篇更加严谨的题解:
显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。
(大力数据结构~)
#include<bits/stdc++.h>
const int MO = ;
const int maxn = ;
const int maxm = ; struct node
{
int fa,top,son,tot;
}a[maxn];
struct QRs
{
int x,id,opt;
QRs(int a=, int b=, int c=):x(a),id(b),opt(c) {}
bool operator < (QRs a) const
{
return x < a.x;
}
}q[maxn<<];
int chain[maxn],chTot;
int f[maxn<<],add[maxn<<];
int edges[maxm],nxt[maxm],head[maxn],edgeTot;
int ansAdd[maxn],ansDel[maxn],qr[maxn];
int n,m,tot; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void addedge(int u, int v)
{
edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
}
void dfs1(int x, int fa)
{
a[x].fa = fa, a[x].son = -, a[x].tot = ;
for (int i=head[x]; i!=-; i=nxt[i])
{
int v = edges[i];
dfs1(v, x), a[x].tot += a[v].tot;
if (a[x].son==-||a[a[x].son].tot < a[v].tot)
a[x].son = v;
}
}
void dfs2(int x, int top)
{
chain[x] = ++chTot, a[x].top = top;
if (a[x].son==-) return;
dfs2(a[x].son, top);
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i]!=a[x].son)
dfs2(edges[i], edges[i]);
}
void pushup(int x)
{
f[x] = (f[x<<]+f[x<<|])%MO;
}
void pushdown(int x, int l, int r)
{
if (add[x]){
add[x<<] += add[x], add[x<<|] += add[x];
f[x<<] += l*add[x], f[x<<|] += r*add[x];
f[x<<] %= MO, f[x<<|] %= MO;
add[x] = ;
}
}
void update(int rt, int L, int R, int l, int r)
{
if (L <= l&&r <= R){
add[rt]++, f[rt] = (f[rt]+r-l+)%MO;
return;
}
int mid = (l+r)>>;
pushdown(rt, mid-l+, r-mid);
if (L <= mid) update(rt<<, L, R, l, mid);
if (R > mid) update(rt<<|, L, R, mid+, r);
pushup(rt);
}
void updateChain(int x, int y)
{
while (a[x].top!=a[y].top)
{
update(, chain[a[x].top], chain[x], , n);
x = a[a[x].top].fa;
}
update(, chain[y], chain[x], , n);
}
int query(int rt, int L, int R, int l, int r)
{
if (L <= l&&r <= R) return f[rt];
int mid = (l+r)>>, ret = ;
pushdown(rt, mid-l+, r-mid);
if (L <= mid) ret += query(rt<<, L, R, l, mid);
if (R > mid) ret += query(rt<<|, L, R, mid+, r);
return ret%MO;
}
int queryChain(int x, int y)
{
int ret = ;
while (a[x].top!=a[y].top)
{
ret += query(, chain[a[x].top], chain[x], , n);
x = a[a[x].top].fa;
}
ret += query(, chain[y], chain[x], , n);
return ret%MO;
}
int main()
{
memset(head, -, sizeof head);
n = read(), m = read();
for (int i=; i<n; i++) addedge(read(), i);
for (int i=; i<=m; i++)
{
q[++tot] = QRs(read()-, i, ), q[++tot] = QRs(read(), i, );
qr[i] = read();
}
std::sort(q+, q+tot+);
dfs1(, -);
dfs2(, );
int now = -;
for (int i=; i<=tot; i++)
{
while (now < q[i].x)
updateChain(++now, );
int id = q[i].id;
if (q[i].opt)
ansAdd[id] = queryChain(qr[id], );
else ansDel[id] = queryChain(qr[id], );
}
for (int i=; i<=m; i++)
printf("%d\n",(ansAdd[i]-ansDel[i]+MO)%MO);
return ;
}
END
【树链剖分 差分】bzoj3626: [LNOI2014]LCA的更多相关文章
- 树链剖分与倍增求LCA
树链剖分与倍增求\(LCA\) 首先我要吐槽机房的辣基供电情况,我之前写了一上午,马上就要完成的时候突然停电,然后\(GG\)成了送链剖分 其次,我没歧视\(tarjan LCA\) 1.倍增求\(L ...
- BZOJ 3626 LCA(离线+树链剖分+差分)
显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已 ...
- HDU 5452——Minimum Cut——————【树链剖分+差分前缀和】ACdream 1429——Diversion——————【树链剖分】
Minimum Cut Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 65535/102400 K (Java/Others)Tota ...
- 洛谷 P3258 [JLOI2014]松鼠的新家 树链剖分+差分前缀和优化
目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 说明 思路 AC代码 优化 优化后AC代码 总结 题面 题目链接 P3258 [JLOI2 ...
- 【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】
还是来致敬一下那过往吧 题目分析 先丢代码 #include<bits/stdc++.h> ; ; ; struct node { int top,son,fa,tot; }a[maxn] ...
- 树链剖分(附带LCA和换根)——基于dfs序的树上优化
.... 有点懒: 需要先理解几个概念: 1. LCA 2. 线段树(熟练,要不代码能调一天) 3. 图论的基本知识(dfs序的性质) 这大概就好了: 定义: 1.重儿子:一个点所连点树size最大的 ...
- [BZOJ3626] [LNOI2014]LCA(树链剖分)
[BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...
- 【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA
引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深 ...
- 【BZOJ3626】LCA(树上差分,树链剖分)
题意:给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给 ...
随机推荐
- Apache与Tomcat的区别和联系
经常在用apache和tomcat等这些服务器,可是总感觉还是不清楚他们之间有什么关系,在用tomcat的时候总出现apache,总感到迷惑,到底谁是主谁是次,因此特意在网上查询了一些这方面的资料,总 ...
- LeetCode.897-递增搜索树(Increasing Order Search Tree)
这是悦乐书的第346次更新,第370篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第211题(顺位题号是897).给定一棵树,按中序遍历顺序重新排列树,以便树中最左边的节 ...
- Rsync备份同步数据工具
Rsync is a fast and extraordinarily versatile file copying tool. Rsync是一款开源的,快速的,多功能的,可实现全量和增量的本地 ...
- c++的substr()函数
substr()函数注意:string str =“Hello”: substr(0,4)=“Hell”,0是起始位置,4是要复制的长度,strlen函数输出的是除了结束符"\0" ...
- 使用命令动态更新JAR包中的文件
动态更新JAR包中的文件,经本人实际测试可正常执行! 一.查询jar包中要替换的文件位置 jar -tvf gateway.jar | grep topjui.config.js 二.在当前 ...
- Cordova 系列之Mac OS 环境配置
1.从AppStore 安装xcode 2.安装node.js环境 http://nodejs.org/ 3.使用命令行安装 cordova 命令行帮助:http://cordova.apache.o ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- spring security 5 There is no PasswordEncoder mapped for the id "null" 错误
转载请注明出处 http://www.cnblogs.com/majianming/p/7923604.html 最近在学习spring security,但是在设置客户端密码时,一直出现了一下错误提 ...
- MVC系列学习(十六)-区域的学习
1.查找控制器的过程 1.1调用其他项目中的控制器 a.先到网站根目录下的bin文件夹下,遍历所有的程序集 b.找到以Controller结尾的类 c.再找出其中继承了Controller的类 d.接 ...
- Java开发笔记(九十六)线程的基本用法
每启动一个程序,操作系统的内存中通常会驻留该程序的一个进程,进程包含了程序的完整代码逻辑.一旦程序退出,进程也就随之结束:反之,一旦强行结束进程,程序也会跟着退出.普通的程序代码是从上往下执行的,遇到 ...