题目链接

看到题目肯定首先想到要求LCA(其实是我菜),可乍一看,n与q的规模为5W,

求LCA的复杂度为\(O(logN)\),那么总时间复杂度为\(O(nq\ log\ n)\)。

怎么搞呢?

会树上差分的都知道,要对一条链进行操作,比如说链上的节点权值\(+p\),就要对两个端点分别\(+p\),然后对\(LCA\)及其父亲分别\(-p\)。

和这个思想差不多,设两个点为\(u, v\),那么求\(dep(LCA(u,v))\)只需要把\((root,u)\)之前的路径所有点权值\(+1\),然后统计\((root,v)\)路径上的权值和就行了,具体请自行脑补我觉得很好理解啊,这个过程显然可以用树剖来完成。

然后就是差分,注意不是树上差分,\(\sum_{i=l}^{r}LCA(i,z)=\sum_{i=1}^{r}LCA(i,z)-\sum_{i=1}^{l-1}LCA(i,z)\)

考虑离线,设每个询问为\((l,r,z)\),定义一个结构体,包含\(x\)(端点),\(k\)(-1为左端点,1为右端点),\(z\)(要求LCA的点),\(id\)(是哪个询问),用2个空间分别存储左端点\(l\)和右端点\(r\),然后按端点编号排序。此时存放询问的数组应该是有\(2q\)个的。定义一个指针\(cur\),表示当前已经把前\(cur\)个点到根的路径\(+1\)了。枚举\(i(1~2q)\),如果是左端点,把\(cur\)更新到\(x-1\),(\(while(cur<x-1) Updata(1,++cur);\)),把\(ans[id]\)减去\(z\)到根的权值和,相当于\(-\sum_{i=1}^{l-1}LCA(i,z)\)。如果是右端点,同理,把\(cur\)更新到\(x\),然后\(ans[id]\)加上...这题就愉快地切掉了。时间复杂度\(O(n\ log^2\ n)\)

手写树剖一遍过,好激动

#include <cstdio>
#include <algorithm>
using std::swap;
using std::sort;
#define left (now << 1)
#define right (now << 1 | 1)
const int MAXN = 50010;
const int MOD = 201314;
struct Edge{
int next, to;
}e[MAXN];
int head[MAXN], num, n, q, a, b, c;
int fa[MAXN], maxson[MAXN], size[MAXN], top[MAXN], dfsx[MAXN], ID, pos[MAXN], dep[MAXN], ans[MAXN];
inline void Add(int from, int to){
e[++num].to = to;
e[num].next = head[from];
head[from] = num;
}
int sum[MAXN << 2], lazy[MAXN << 2];
inline void pushdown(int now, int len){
if(lazy[now]){
sum[left] += lazy[now] * (len - (len >> 1));
sum[right] += lazy[now] * (len >> 1);
lazy[left] += lazy[now];
lazy[right] += lazy[now];
lazy[now] = 0;
}
}
inline void pushup(int now){
sum[now] = sum[left] + sum[right];
}
/*void Build(int now, int l, int r){
if(l != r){
int mid = (l + r) >> 1;
Build(left, l, mid);
Build(right, mid + 1, r);
pushup(now);
}
else sum[now] = a[l];
}*/
void Change(int now, int l, int r, int wl, int wr, int p){
if(l > wr || r < wl) return ;
if(l >= wl && r <= wr){
sum[now] += p * (r - l + 1); lazy[now] += p; return ;
}
pushdown(now, r - l + 1);
int mid = (l + r) >> 1;
Change(left, l, mid, wl, wr, p);
Change(right, mid + 1, r, wl, wr, p);
pushup(now);
}
int Query(int now, int l, int r, int wl, int wr){
if(l > wr || r < wl) return 0;
if(l >= wl && r <= wr) return sum[now];
pushdown(now, r - l + 1);
int ans = 0;
int mid = (l + r) >> 1;
ans += Query(left, l, mid, wl, wr);
ans += Query(right, mid + 1, r, wl, wr);
return ans;
}
struct ASK{
int x, z, k, id;
bool operator < (const ASK &A) const{ //按照端点排序
return x == A.x ? k < A.k : x < A.x;
}
}Ans[MAXN << 1];
void dfs1(int u){
dep[u] = dep[fa[u]] + 1;
size[u] = 1;
for(int i = head[u]; i; i = e[i].next){
dfs1(e[i].to);
size[u] += size[e[i].to];
if(size[e[i].to] > size[maxson[u]])
maxson[u] = e[i].to;
}
}
void dfs2(int u, int t){
dfsx[++ID] = u;
top[u] = t;
if(maxson[u]) dfs2(maxson[u], t);
for(int i = head[u]; i; i = e[i].next){
if(e[i].to != maxson[u])
dfs2(e[i].to, e[i].to);
}
}
void Updata(int u, int v, int p){
int x = top[u], y = top[v];
while(x != y){
if(dep[x] > dep[y]) swap(x, y), swap(u, v);
Change(1, 1, n, pos[y], pos[v], p);
u = fa[x]; x = top[u];
v = fa[y]; y = top[v];
}
if(dep[u] > dep[v]) swap(u, v);
Change(1, 1, n, pos[u], pos[v], p);
}
int Solve(int u, int v){
int ans = 0;
int x = top[u], y = top[v];
while(x != y){
if(dep[x] > dep[y]) swap(x, y), swap(u, v);
ans += Query(1, 1, n, pos[y], pos[v]);
u = fa[x]; x = top[u];
v = fa[y]; y = top[v];
}
if(dep[u] > dep[v]) swap(u, v);
ans += Query(1, 1, n, pos[u], pos[v]);
return ans;
}
int cur;
int main(){
scanf("%d%d", &n, &q);
for(int i = 2; i <= n; ++i)
scanf("%d", &fa[i]), Add(fa[i] += 1, i);
for(int i = 1; i <= q; ++i){
scanf("%d%d%d", &a, &b, &c);
Ans[i].x = a + 1;
Ans[i + q].x = b + 1;
Ans[i].z = Ans[i + q].z = c + 1;
Ans[i].k = -1;
Ans[i + q].k = 1;
Ans[i].id = Ans[i + q].id = i;
}
dfs1(1);
dfs2(1, 1);
for(int i = 1; i <= n; ++i)
pos[dfsx[i]] = i;
sort(Ans + 1, Ans + q * 2 + 1);
for(int i = 1; i <= 2 * q; ++i){
if(Ans[i].k < 0) //是左端点
while(cur < Ans[i].x - 1)
Updata(1, ++cur, 1);
else //是右端点
while(cur < Ans[i].x)
Updata(1, ++cur, 1);
ans[Ans[i].id] += Ans[i].k * Solve(1, Ans[i].z);
}
for(int i = 1; i <= q; ++i)
printf("%d\n", (ans[i] + MOD) % MOD);
return 0;
}

【洛谷 P4211】[LNOI2014]LCA(树链剖分,差分)的更多相关文章

  1. 洛谷 P4211 [LNOI2014]LCA (树链剖分+离线)

    题目:https://www.luogu.org/problemnew/solution/P4211 相当难的一道题,其思想难以用言语表达透彻. 对于每个查询,区间[L,R]中的每个点与z的lca肯定 ...

  2. 洛谷$P4211\ [LNOI2014]\ LCA$ 树链剖分+线段树

    正解:树剖+线段树 解题报告: 传送门$QwQ$ 看到$dep[lca]$啥的就想到之前托腮腮$CSP$模拟$D1T3$的那个套路,,, 然后试下这个想法,于是$dep[lca(x,y)]=\sum_ ...

  3. [BZOJ3626] [LNOI2014]LCA(树链剖分)

    [BZOJ3626] [LNOI2014]LCA(树链剖分) 题面 给出一棵N个点的树,要求支持Q次询问,每次询问一个点z与编号为区间[l,r]内的点分别求最近公共祖先得到的最近公共祖先深度和.N, ...

  4. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  5. BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )

    说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...

  6. BZOJ2243 洛谷2486 [SDOI2011]染色 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2243 题目传送门 - 洛谷2486 题意概括 一棵树,共n个节点. 让你支持以下两种操作,共m次操 ...

  7. BZOJ3626[LNOI2014]LCA——树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  8. bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

  9. 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询 ...

  10. 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)

    题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...

随机推荐

  1. 链接程序的时候遇到问题:fatal error LNK1104: cannot open file 'rctrl-d.lib'

    1.lib库文件没有添加到工程中(工程里面根本就没有这个文件) 2.

  2. vs编译报错 BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

    1.重复释放内存导致,new delete和malloc free两个组合分配的堆空间都不能重复释放两次: 2.用delete或者free释放栈空间导致内存空间被破坏(栈空间内存的头部有系统写入的一些 ...

  3. Android之线程安全的单例模式,Adapter注意事项之引用传值

    线程安全的单例模式单位模式一般写法如下: public static FestivalLab mInstance; private FestivalLab() { } public static Fe ...

  4. Scala学习笔记(三):基础知识

    有了可运行的环境,就需要写一些简单的语句来认识一下Scala,本文没有写那么详细,只是为了方便查看.唤起回忆 (1)变量的定义方法 Scala有两种变量 var val 注意:在解释器中,可以用一个之 ...

  5. js复制粘贴事件

    一.相应的事件 copy: 在发生复制操作时触发. beforecut: 在发生剪切操作 前 触发. cut: 在 发生 剪切 操作 时 触发. beforepaste: 在 发生 粘贴 操作 前 触 ...

  6. Ubuntu下使用Git_6

    这回真的是最后一篇了,哈哈,改写提交. 这里这部分在目前的学习阶段还没有用到,所以,这里将不在有实验的部分,在下面的链接中有详细的介绍 这也是我第一讲一个网站的内容完成的学习完成,这这部分,我讲简单的 ...

  7. 虚拟现实-VR-UE4-创建C++版工程

    首先,创建C++版本的UE4 项目工程,我使用的是4.12.3版本,据了解,新版本后面的编译都是vs2015 所以,想要创建C++版本的工程,就需要安装vs2015 至于vs2015的安装,自己百度吧 ...

  8. mysql数据库----Pymysql

    本节重点: pymysql下载和使用 sql注入 增.删.改:conn.commit() 查:fetchone.fetchmany.fetchall 一.pymysql的下载和使用 之前我们都是通过M ...

  9. Visual Studio各版本工程文件之间的转换 [转载]

    原网址:http://www.cnblogs.com/jmliao/p/5594179.html Visual Studio各版本工程文件之间的转换   由于VS版本比较多,低版本无法直接打开高版本的 ...

  10. BZOJ 4011 HNOI2015 落忆枫音 DAG上的dp(实际上重点在于分析)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4011 题意概述:给出一张N点的DAG(从1可以到达所有的点),点1的入度为0.现在加一条原 ...