题目描述

给出一个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的最近公共祖先的深度之和)

输入

第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。

输出

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

样例输入

5 2
0
0
1
1
1 4 3
1 4 2

样例输出

8
5


题解

树链剖分+线段树

考虑两点LCA的深度,可以看作两个点到根节点的路径交的长度(点的个数)。

而路径交的长度,又可以看作把一条路径上的点权值+1,然后查询另一条路径上的点的权值和。

于是本题转化为:把编号在$[l,r]$内的所有点到根路径上的点权值+1,再查询z到根的点权和。

于是我们可以把问题转化为前缀相减的形式,即求编号在$[1,p]$内的所有点到根路径上的点权值+1,查询z到根的点权和。

将拆成前缀相减后的询问离线,按照$p$排序。按照顺序直接处理对应编号,再查询即可。此时需要支持链上修改、链上查询,使用树链剖分+线段树即可。

时间复杂度$O(n\log^2n)$

#include <cstdio>
#include <algorithm>
#define N 50010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
struct data
{
int p , z , v , id;
data() {}
data(int P , int Z , int V , int Id) {p = P , z = Z , v = V , id = Id;}
bool operator<(const data &a)const {return p < a.p;}
}a[N << 1];
int n , head[N] , to[N] , next[N] , cnt , fa[N] , si[N] , bl[N] , pos[N] , tot , sum[N << 2] , tag[N << 2] , ans[N];
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs1(int x)
{
int i;
si[x] = 1;
for(i = head[x] ; i ; i = next[i])
dfs1(to[i]) , si[x] += si[to[i]];
}
void dfs2(int x , int c)
{
int i , k = n;
bl[x] = c , pos[x] = ++tot;
for(i = head[x] ; i ; i = next[i])
if(si[to[i]] > si[k])
k = to[i];
if(k != n)
{
dfs2(k , c);
for(i = head[x] ; i ; i = next[i])
if(to[i] != k)
dfs2(to[i] , to[i]);
}
}
inline void pushdown(int l , int r , int x)
{
if(tag[x])
{
int mid = (l + r) >> 1;
sum[x << 1] += tag[x] * (mid - l + 1) , tag[x << 1] += tag[x];
sum[x << 1 | 1] += tag[x] * (r - mid) , tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
}
void update(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e)
{
sum[x] += r - l + 1 , tag[x] ++ ;
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) update(b , e , lson);
if(e > mid) update(b , e , rson);
sum[x] = sum[x << 1] + sum[x << 1 | 1];
}
int query(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return sum[x];
pushdown(l , r , x);
int mid = (l + r) >> 1 , ans = 0;
if(b <= mid) ans += query(b , e , lson);
if(e > mid) ans += query(b , e , rson);
return ans;
}
void modify(int x)
{
while(bl[x]) update(pos[bl[x]] , pos[x] , 1 , n , 1) , x = fa[bl[x]];
update(1 , pos[x] , 1 , n , 1);
}
int solve(int x)
{
int ans = 0;
while(bl[x]) ans += query(pos[bl[x]] , pos[x] , 1 , n , 1) , x = fa[bl[x]];
return ans + query(1 , pos[x] , 1 , n , 1);
}
int main()
{
int m , i , l , r , x , h = 0;
scanf("%d%d" , &n , &m);
for(i = 1 ; i < n ; i ++ ) scanf("%d" , &fa[i]) , add(fa[i] , i);
dfs1(0) , dfs2(0 , 0);
for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &l , &r , &x) , a[i] = data(l - 1 , x , -1 , i) , a[i + m] = data(r , x , 1 , i);
sort(a + 1 , a + 2 * m + 1);
for(i = 1 ; i <= 2 * m ; i ++ )
{
while(h <= a[i].p) modify(h++);
ans[a[i].id] += a[i].v * solve(a[i].z);
}
for(i = 1 ; i <= m ; i ++ ) printf("%d\n" , ans[i] % 201314);
return 0;
}

【bzoj3626】[LNOI2014]LCA 树链剖分+线段树的更多相关文章

  1. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  2. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  3. bzoj2243[SDOI2011]染色 树链剖分+线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 9012  Solved: 3375[Submit][Status ...

  4. BZOJ3862Little Devil I——树链剖分+线段树

    题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...

  5. BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树

    题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...

  6. BZOJ2819Nim——树链剖分+线段树+Nim游戏

    题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略 ...

  7. POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 )

    POJ.2763 Housewife Wind ( 边权树链剖分 线段树维护区间和 ) 题意分析 给出n个点,m个询问,和当前位置pos. 先给出n-1条边,u->v以及边权w. 然后有m个询问 ...

  8. BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

  9. [HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

    题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...

随机推荐

  1. 使用Cygwin在WIN系统下处理文本常用命令

    1.打开Cygwin,把需要处理的文本复制你的安装目录例如:D:\cygwin\home\Administrator 使用 ls命令查看根目录文件 2.现在我们就可以对1.txt文本进行操作, 3.我 ...

  2. python核心编程2 第十章 练习

    10-6.改进的open().为内建的open()函数创建一个封装.使得成功打开文件后,返回文件句柄:若打开失败则返回给调用者None, 而不是生成一个异常.这样你打开文件就不需要额外的异常处理语句. ...

  3. raid概述与CentOS7.4中raid5的搭建与测试

    前言 一.raid的定义与作用 raid(独立冗余磁盘阵列).raid技术通过把多个硬盘设备组合成一个容量更大的,安全性更好的磁盘阵列.把数据切割成许多区段后分别放在不同的物理磁盘上,然后利用分散读写 ...

  4. Co. - Microsoft - Windows - 快捷键

    [F1-F12]键 [Win]键 Windows键就是电脑键盘上是Windows图标的键,一般在Ctrl键和Alt键之间.搜索看整个键盘你会发现键盘上有两个Windows键,主要是左右手设计方便按各种 ...

  5. tarnado源码解析系列一

    目录 tarnado tarnado源码安装 tarnado测试程序 application类的解析 一. tarnado简介 最近在学习Python,无意间接触到的tarnado,感觉tarnado ...

  6. 清华大学《C++语言程序设计基础》线上课程笔记02---类与对象

    类与对象 public是类的对外访问接口: 类内初始值 在定义类时对数据成员写初始值,在创建对象的时候,会使用类内初始值初始化数据成员: class Clock { public: void show ...

  7. mac制作U盘启动器

    Infi-chu: http://www.cnblogs.com/Infi-chu/ 一.所需工具及必要条件: 1. 首先需要一个大于16GB U盘. 2.电脑系统版本应该大于10.11.X(因为之前 ...

  8. Python自动化运维——文件内容差异对比

    Infi-chu: http://www.cnblogs.com/Infi-chu/ 模块:difflib 安装:Python版本大于等于2.3系统自带 功能:对比文本之间的差异,而且支持输出可读性比 ...

  9. C++ vector二维数组

    C++ 构建二维动态数组 int **p; p = ]; //注意,int*[10]表示一个有10个元素的指针数组 ; i < ; ++i) { p[i] = ]; } 这样就构成10*5的数组 ...

  10. 如何在windows“我的电脑”中添加快捷文件夹

    如图所示,windows中打开“我的电脑”时,原来有6个默认的文件夹,访问非常便捷,自己想再增加,可以使用“ThisPCTweaker”即可完成 操作如下图,不多解释,简单操作: 文件下载:http: ...