『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 Format
第一行2个整数n q。 接下来n-1行,分别表示点1到点n-1的父节点编号。 接下来q行,每行3个整数l r z。
Output Format
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
Sample Input
5 2
0
0
1
1
1 4 3
1 4 2
Sample Output
8
5
解析
容易发现一个询问可以拆成两个询问:
\]
都是前缀和形式的,方便我们处理。
对于一个前缀和询问,我们可以发现它的贡献具有一个实际意义:也就是求每一个\(i\)与\(z\)公共祖先的个数。进一步,我们可以这样理解:把每一个节点\(i\)到根的路径上的点点权都加一,求\(z\)到更路径上的点权和。
我们都知道直接树上加链和查询链可以用树链剖分,但是各个询问之间好像相互有影响。
最简单的解决方法就是把询问离线。我们之前已经把询问转化为前缀和的形式了,那就把所有询问按照\(r\)排序,这样依次处理询问就没有影响了。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 50020 , Mod = 201314;
struct node { int l,r,tag; long long cnt; };
struct SegmentTree
{
node ver[N<<2];
#define l(p) ver[p].l
#define r(p) ver[p].r
#define cnt(p) ver[p].cnt
#define tag(p) ver[p].tag
inline void build(int p,int l,int r)
{
l(p) = l , r(p) = r , cnt(p) = tag(p) = 0;
if ( l == r ) return;
int mid = l + r >> 1;
build( p<<1 , l , mid );
build( p<<1|1 , mid+1 , r );
}
inline void update(int p) { cnt(p) = cnt(p<<1) + cnt(p<<1|1); }
inline void spread(int p)
{
if ( tag(p) )
{
tag(p<<1) = ( tag(p<<1) + tag(p) ) % Mod;
tag(p<<1|1) = ( tag(p<<1|1) + tag(p) ) % Mod;
cnt(p<<1) = ( cnt(p<<1) + 1LL * ( r(p<<1) - l(p<<1) + 1 ) * tag(p) % Mod ) % Mod;
cnt(p<<1|1) = ( cnt(p<<1|1) + 1LL * ( r(p<<1|1) - l(p<<1|1) + 1 ) * tag(p) % Mod ) % Mod;
tag(p) = 0;
}
}
inline void modify(int p,int l,int r,int v)
{
if ( l <= l(p) && r >= r(p) )
{
cnt(p) = ( cnt(p) + ( r(p) - l(p) + 1 ) * v % Mod ) % Mod;
tag(p) = ( tag(p) + v ) % Mod; return;
}
spread( p );
int mid = l(p) + r(p) >> 1;
if ( l <= mid ) modify( p<<1 , l , r , v );
if ( r > mid ) modify( p<<1|1 , l , r , v );
update( p );
}
inline int query(int p,int l,int r)
{
if ( l <= l(p) && r >= r(p) ) return cnt(p);
spread( p );
int mid = l(p) + r(p) >> 1 , res = 0;
if ( l <= mid ) res = ( res + query( p<<1 , l , r ) ) % Mod;
if ( r > mid ) res = ( res + query( p<<1|1 , l , r ) ) % Mod;
return res;
}
};
SegmentTree Tree;
struct edge { int ver,next; } e[N*2];
struct query { int r,z,id,f; } a[N*2];
int n,m,t,Head[N],tot;
int fa[N],dep[N],son[N],size[N],top[N],id[N],cnt;
long long ans[N];
inline void insert(int x,int y)
{
e[++t] = (edge){y,Head[x]} , Head[x] = t;
e[++t] = (edge){x,Head[y]} , Head[y] = t;
}
inline int read(void)
{
int x = 0 , w = 0; char ch = ' ';
while ( !isdigit(ch) ) w |= ch=='-' , ch = getchar();
while ( isdigit(ch) ) x = x*10 + ch-48 , ch = getchar();
return w ? -x : x;
}
inline void input(void)
{
n = read() , m = read();
for ( int i = 2 ; i <= n ; i++ )
insert( i , read() + 1 );
for ( int i = 1 ; i <= m ; i++ )
{
int l = read() + 1 , r = read() + 1 , z = read() + 1;
a[++tot] = (query){ r , z , i , 1 };
a[++tot] = (query){ l-1 , z , i , -1 };
}
}
inline void dfs1(int x,int f,int depth)
{
fa[x] = f , dep[x] = depth , size[x] = 1;
int Max = -1;
for ( int i = Head[x] ; i ; i = e[i].next )
{
int y = e[i].ver;
if ( y == f ) continue;
dfs1( y , x , depth+1 );
size[x] += size[y];
if ( size[y] > Max ) Max = size[y] , son[x] = y;
}
}
inline void dfs2(int x,int Top)
{
id[x] = ++cnt , top[x] = Top;
if ( !son[x] ) return;
else dfs2( son[x] , Top );
for ( int i = Head[x] ; i ; i = e[i].next )
{
int y = e[i].ver;
if ( y == fa[x] || y == son[x] ) continue;
dfs2( y , y );
}
}
inline void modify_chain(int x,int y,int val)
{
while ( top[x] ^ top[y] )
{
if ( dep[top[x]] < dep[top[y]] ) swap( x , y );
Tree.modify( 1 , id[top[x]] , id[x] , val );
x = fa[top[x]];
}
if ( dep[x] > dep[y] ) swap( x , y );
Tree.modify( 1 , id[x] , id[y] , val );
}
inline int query_chain(int x,int y)
{
int res = 0;
while ( top[x] ^ top[y] )
{
if ( dep[top[x]] < dep[top[y]] ) swap( x , y );
res = ( res + Tree.query( 1 , id[top[x]] , id[x] ) ) % Mod;
x = fa[top[x]];
}
if ( dep[x] > dep[y] ) swap( x , y );
return ( res + Tree.query( 1 , id[x] , id[y] ) ) % Mod;
}
inline bool compare(query p1,query p2) { return p1.r < p2.r; }
inline void solve(void)
{
int p = 0;
for ( int i = 1 ; i <= tot ; i++ )
{
while ( p < a[i].r ) modify_chain( 1 , ++p , 1 );
ans[ a[i].id ] += a[i].f * query_chain( 1 , a[i].z );
ans[ a[i].id ] = ( ans[ a[i].id ] % Mod + Mod ) % Mod;
}
}
int main(void)
{
input();
dfs1( 1 , 0 , 1 );
dfs2( 1 , 1 );
Tree.build( 1 , 1 , n );
sort( a+1 , a+tot+1 , compare );
solve();
for (int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
<后记>
『LCA 树链剖分』的更多相关文章
- Count on a tree SPOJ 10628 主席树+LCA(树链剖分实现)(两种存图方式)
Count on a tree SPOJ 10628 主席树+LCA(树链剖分实现)(两种存图方式) 题外话,这是我第40篇随笔,纪念一下.<( ̄︶ ̄)↗[GO!] 题意 是说有棵树,每个节点上 ...
- [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 ...
- Codeforces Round #329 (Div. 2) D. Happy Tree Party LCA/树链剖分
D. Happy Tree Party Bogdan has a birthday today and mom gave him a tree consisting of n vertecie ...
- BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )
说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...
- [CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)
Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花 ...
- 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 ...
- LCA树链剖分
LCA(Lowest Common Ancestor 最近公共祖先)定义如下:在一棵树中两个节点的LCA为这两个节点所有的公共祖先中深度最大的节点. 比如这棵树 结点5和6的LCA是2,12和7的LC ...
随机推荐
- AI:WEB:1 Walkthrough
AI: Web: 1 Vulnhub Walkthrough靶机下载:https://www.vulnhub.com/entry/ai-web-1,353/测试方法: Nmap网络扫描 浏 ...
- android studio学习---Android studio 导入github工程
无论是那种方式,都最好是先把github上的工程项目下载到本地,然后修改文件再import 首先要知道 自己的build.gradle,在project下面的版本号是多少,比如我的: depende ...
- WorkFlow三:CLASS事件触发工作流
1.创建关键字段结构.这里没有新建,使用前面创建的结构: 2.SE24创建类:保存激活. 3.接口里添加IF_WORKFLOW并激活.(其他两个激活就出现了,不用管) 4.在属性页签中定义两个属性,其 ...
- `Java`中`abstract class`与`interface`区别
abstract class Java中允许使用abstract修饰符声明方法,此时只定义方法但是不实现方法(abstract修饰的方法没有主体,只有一个签名和一个分号). 以下是abstract方法 ...
- django-orm 快速清理migrations缓存
Shell #!/bin/bash Project_dir=`pwd` find $Project_dir -type d -a -name 'migrations' \ -exec rm -rf { ...
- request有get,post,put,delete等方法大全
注:本文为个人学习摘录,原文地址为:http://javaeedevelop.iteye.com/blog/1725299 An HTTP request is a class consisting ...
- logging.basicConfig配置文件
import sys, logging logging.basicConfig(level=logging.INFO, # 日志等级 # filename: 指定日志文件名 format='level ...
- blocking cache和non-blocking cache
- a Blocking Cache will not accept any more request until the miss is taken care of. - a Non-blockin ...
- Struts CRUD
Struts CRUD 利用struts完成增删改查 思路: 1.导入相关的pom依赖(struts.自定义标签库的依赖) 2.分页的tag类导入.z.tld.完成web.xml的配置 3.dao层去 ...
- ORM常用的13个方法
介绍一个可以以py脚本方式运行ORM操作的方法: 可在项目内新建个py文件,复制项目内manage.py文件中的以下代码: if __name__ == "__main__": o ...