【树链剖分 差分】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次询问,每次询问给 ...
随机推荐
- 笔记-JavaWeb学习之旅9
XML Extensible Markup Language 可扩展标记语言 功能:配置文件,在网络中传输 基本语法 1.xml文档的后缀名.xml 2.xml第一行必须定义为文档声明 3.xml文档 ...
- 黑马Stream流学习 Stream流 函数式接口 Lambda表达式 方法引用
- 整理一下 通知传值 Block传值
Block: 一. (1) 在需要传值的界面定义属性 // 点击collectionViewCell的回调 @property (nonatomic, copy) void(^Didcollectio ...
- Django的ORM基础增删改查
查询 all() 返回模型类对应表格中所有数据,返回查询集 get() 返回表格中满足条件的一条且只能有一条数据 如果查到多条数据,则抛异常:MultipleObjectsReturned 查询不到数 ...
- 对shell的简单认识
shell是一个命令解释器: shell分为交互式shell和非交互式shell: 交互式shell就是命令行一问一答:非交互式shell是像shell文本那样,一次解析文本, 并未在命令行给我们作出 ...
- 如何在asp.net mvc中添加自定义的HTML辅助种方法
很久没在博客园发表文章了,今天来总结一下如何在asp.net mvc中添加自定义的HTML辅助方法.我们现在设计这么一个目前,利用自定义的HTML方法来渲染一个普通的img标记.直接进入主题吧: 首先 ...
- SVN中如何去除版本控制器
SVN,大家都熟悉,做项目都知道,不知道你有没有遇到每次提交的代码的时候,都会把bin和obj自动生成的文件夹提交SVN服务器上 其实这里都不需要提交,每次生成都提交,可能还会容易冲突,如何不让bin ...
- Django2.0路由补充之path,re_path及视图层
以下是Django2.0版本 正则捕获到的参数都是字符串,所以如果函数需要用的其他数据类型,可以在函数中直接转换,也可以在路由中直接转换,如下: 下面实例是匹配整数,传过去的参数就是整数 from d ...
- poj2135 最小费用流
添加超级源点(与点1之间的边容量为2,权值为0)和超级汇点(与点N之间的边容量为2,权值为0),求流量为2的最小费用流.注意是双向边. #include <iostream> #inclu ...
- MediaRecord一些使用记录
今天学习了MediaRecord的使用,第一次使用做个记录. MediaRecord作用是声音录制,使用步骤如下: 1.新建出音频文件代码如下: 先创建出用于存储音频文件 File dir = new ...