https://www.luogu.org/problemnew/show/P4211

baoli

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring> using namespace std;
const int N = 5e4 + ;
const int Mod = ; #define gc getchar() #define RR freopen("gg.in", "r", stdin) int fa[N] = {-}, deep[N], cnt[N], head[N];
struct Node {int u, v, nxt;} G[N];
int n, Ty, now = ; inline int read() {
int x = ; char c = gc;
while(c < '' || c > '') c = gc;
while(c >= '' && c <= '') x = x * + c - '', c = gc;
return x;
} inline void Add(int u, int v) {
G[now].u = u; G[now].v = v; G[now].nxt = head[u]; head[u] = now ++;
} void Dfs(int u, int dep) {
deep[u] = dep;
for(int i = head[u]; ~ i; i = G[i].nxt) {
int v = G[i].v;
if(v != fa[u]) Dfs(v, dep + );
}
} void Dfs_2(int u) {
if(head[u] == -) return ;
for(int i = head[u]; ~ i; i = G[i].nxt) {
int v = G[i].v;
if(v != fa[u]) {
Dfs_2(v);
cnt[u] += cnt[v];
}
}
} int main() {
n = read();
Ty = read();
for(int i = ; i < n; i ++) head[i] = -;
for(int i = ; i < n; i ++) {
int u = read(); fa[i] = u;
Add(u, i); Add(i, u);
}
Dfs(, );
while(Ty --) {
int l = read(); int r = read(); int z = read();
for(int i = l; i <= r; i ++) cnt[i] ++;
Dfs_2();
int imp = z;
int Answer = ;
while(fa[imp] != -) {
Answer += cnt[imp];
Answer %= Mod;
imp = fa[imp];
} Answer += cnt[imp];
Answer %= Mod;
cout << Answer << endl;
memset(cnt, , sizeof cnt);
}
return ;
}

考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和。

这个方法是可以叠加的,这是非常有用的一点。如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和。

不仅满足可加性,还满足可减性,这就更好了!

那么我们就可以对每个询问 [l, r] 做一个差分,用 Query(r) - Query(l - 1) 作为答案。这样就有一种离线算法:将 n 个点依次操作,将其到 Root 的路径上的点权值 +1 ,然后如果这个点是某个询问的 l - 1 或 r ,就用那个询问的 c 求一下到 Root 的路径和,算入答案中。

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 50005
#define M 201314
#define K 150005
using namespace std;
struct graph {
int nxt,to;
} e[N];
struct quest {
int x,z,n,ans;
} l[N],r[N];
struct linetree {
int l,r,s,len,lzy;
} lt[K];
int g[N],n,q,t1,t2,cnt;
int f[N],p[N],dep[N],top[N],siz[N],son[N];
inline int read() {
int ret=;
char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c)) {
ret=(ret<<)+(ret<<)+c-'';
c=getchar();
}
return ret;
}
inline void addedge(int x,int y) {
e[++cnt].nxt=g[x];
g[x]=cnt;
e[cnt].to=y;
}
inline void dfs1(int u) {
int m=;
siz[u]=;
for(int i=g[u]; i; i=e[i].nxt) {
f[e[i].to]=u;
dep[e[i].to]=dep[u]+;
dfs1(e[i].to);
siz[u]+=siz[e[i].to];
if(siz[e[i].to]>m) {
son[u]=e[i].to;
m=siz[e[i].to];
}
}
}
inline void dfs2(int u,int tp) {
top[u]=tp;
p[u]=++cnt;
if(son[u]) dfs2(son[u],tp);
for(int i=g[u]; i; i=e[i].nxt)
if(e[i].to!=son[u])
dfs2(e[i].to,e[i].to);
}
inline void build(int u,int l,int r) {
lt[u].l=l;
lt[u].r=r;
lt[u].len=lt[u].r-lt[u].l+;
if(lt[u].l<lt[u].r) {
int lef=u<<,rig=u<<|;
int mid=lt[u].l+lt[u].r>>;
build(lef,l,mid);
build(rig,mid+,r);
}
}
inline int cover(int u,int l,int r) {
if(lt[u].l>=l&&lt[u].r<=r) {
++lt[u].lzy;
lt[u].s=(lt[u].s+lt[u].len)%M;
} else if(lt[u].l<lt[u].r) {
int lef=u<<,rig=u<<|;
int mid=lt[u].l+lt[u].r>>;
if(lt[u].lzy) {
lt[lef].lzy+=lt[u].lzy;
lt[rig].lzy+=lt[u].lzy;
lt[lef].s=(lt[lef].s+lt[lef].len*lt[u].lzy)%M;
lt[rig].s=(lt[rig].s+lt[rig].len*lt[u].lzy)%M;
lt[u].lzy=;
}
if(l<=mid) cover(lef,l,r);
if(r>mid) cover(rig,l,r);
lt[u].s=(lt[lef].s+lt[rig].s)%M;
}
}
inline int ask(int u,int l,int r) {
if(lt[u].l>=l&&lt[u].r<=r)
return lt[u].s;
if(lt[u].l<lt[u].r) {
int lef=u<<,rig=u<<|,ret=;
int mid=lt[u].l+lt[u].r>>;
if(lt[u].lzy) {
lt[lef].lzy+=lt[u].lzy;
lt[rig].lzy+=lt[u].lzy;
lt[lef].s=(lt[lef].s+lt[lef].len*lt[u].lzy)%M;
lt[rig].s=(lt[rig].s+lt[rig].len*lt[u].lzy)%M;
lt[u].lzy=;
}
if(l<=mid) ret=(ret+ask(lef,l,r))%M;
if(r>mid) ret=(ret+ask(rig,l,r))%M;
return ret;
}
}
inline void add(int x,int y) {
int t;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) {
t=x;
x=y;
y=t;
}
cover(,p[top[x]],p[x]);
x=f[top[x]];
}
if(p[x]>p[y]) {
t=x;
x=y;
y=t;
}
cover(,p[x],p[y]);
}
inline int que(int x,int y) {
int ret=,t;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) {
t=x;
x=y;
y=t;
}
ret=(ret+ask(,p[top[x]],p[x]))%M;
x=f[top[x]];
}
if(p[x]>p[y]) {
t=x;
x=y;
y=t;
}
ret=(ret+ask(,p[x],p[y]))%M;
return ret;
}
inline bool cmp1(quest x,quest y) {
return x.x<y.x;
} inline bool cmp2(quest x,quest y) {
return x.n<y.n;
}
inline void Aireen() {
n=read();
q=read();
for(int i=,j; i<=n; ++i) {
j=read()+;
addedge(j,i);
}
for(int i=; i<=q; ++i) {
l[i].n=r[i].n=i;
l[i].x=read();
r[i].x=read()+;
l[i].z=r[i].z=read()+;
}
sort(l+,l++q,cmp1);
sort(r+,r++q,cmp1);
while(t1<=q&&!l[t1].x) ++t1;
while(t2<=q&&!r[t2].x) ++t2;
dep[]=;
dfs1();
cnt=;
dfs2(,);
build(,,n);
for(int i=; i<=n; ++i) {
add(,i);
while(t1<=q&&l[t1].x==i) {
l[t1].ans=que(,l[t1].z);
++t1;
}
while(t2<=q&&r[t2].x==i) {
r[t2].ans=que(,r[t2].z);
++t2;
}
}
sort(l+,l++q,cmp2);
sort(r+,r++q,cmp2);
for(int i=; i<=q; ++i)
printf("%d\n",(r[i].ans-l[i].ans+M)%M);
}
int main() {
Aireen();
fclose(stdin);
fclose(stdout);
return ;
}

[Luogu] LCA的更多相关文章

  1. 【OI】倍增求LCA

    ╭(′▽`)╯ 总之,我们都知道lca是啥,不需要任何基础也能想出来怎么用最暴力的方法求LCA,也就是深度深的点先跳到深度浅的点的同一深度,然后一起向上一步步跳.这样显然太慢了! 所以我们要用倍增,倍 ...

  2. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  3. Luogu P4211 [LNOI2014]LCA

    我去这道题的Luogu评级是假的吧,这都算黑题. 我们首先考虑把操作离线不强制在线的题目离线一下一般都要方便些 考虑差分,我们用\(f(x)\)表示\([1,x]\)之间的点与\(z\)的答案,那么显 ...

  4. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  5. Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)

    Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树) Description 一棵树上有n个节点,编号分别 ...

  6. 【Luogu P3379】LCA问题的倍增解法

    Luogu P3379 题意:对于两个节点,寻找他们的最近公共祖先. 一个显而易见的解法是对于每一个节点我们都往上遍历一遍,记录下它每一个祖先,然后再从另一个节点出发,一步一步往上走,找到以前记录过第 ...

  7. [luogu]P1600 天天爱跑步[LCA]

    [luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...

  8. 货车运输 noip2013 luogu P1967 (最大生成树+倍增LCA)

    luogu题目传送门! 首先,题目让我们求每个货车的最大运输量,翻译一下就是求路径上边权最小的边. 利用一下贪心思想可知,所有货车肯定都会尽量往大的边走. 进一步翻译,即为有一些小边货车根本不会走,或 ...

  9. [NOIP 2016D2T2/Luogu P1600] 天天爱跑步 (LCA+差分)

    待填坑 Code //Luogu P1600 天天爱跑步 //Apr,4th,2018 //树上差分+LCA #include<iostream> #include<cstdio&g ...

随机推荐

  1. Mediawiki 子页链接无效的问题

    添加下面的配置到 LocalSettings.php 中即可: # Enable subpages in the main namespace $wgNamespacesWithSubpages[NS ...

  2. React/react相关小结

    React React组件由React元素组成,React组件使用React.Component或React.PureComponent来生成:React元素使用JSX的语法来编写或使用React.c ...

  3. Ajax的学习

    AJAX的学习 AJAX的简介 AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX ...

  4. js入门第二篇之流程控制语句

    表达式语句: 一个表达式可以产生一个值,有可能是运算.函数调用 字面量 表达式可以放在任何需要值的地方. 语句: 语句可以理解成一个行为,循环语句和判断语句就是典型的语句,一个程序有多个语句组成. 流 ...

  5. 什么是DDOS

    什么是DDOS?分布式拒绝服务攻击(Distributed Denial of Service).百度的解释有一个形象的例子我认为比较好理解,照搬如下:   一群恶霸试图让对面那家有着竞争关系的商铺无 ...

  6. Go 字符串使用

    Go语言中的字符串 Go 中的字符串是兼容 Unicode 编码的,并且使用 UTF-8 进行编码,这样每一个Unicode字符根据代码点的大小使用1.2.3或4个字节表示代码点.Go 语言中的字符串 ...

  7. TreeMap源码实现类中文全解析

    /** * 基于红黑树(Red-Black tree)的 NavigableMap 实现.该映射根据其键的自然顺序进行排序, * 或者根据创建映射时提供的Comparator 进行排序,具体取决于使用 ...

  8. 5.Redis的持久化

    Redis中数据的持久化有两种方式:RDB(Redis DataBsse) 和 AOF(Append Only File),默认采取的是RDB方式 RDB 1.是什么:在指定的时间间隔内将内存中的数据 ...

  9. UMI.js开发知识总结

    五分钟掌握最小知识体系 本文阅读时间大概为5分钟,但是能让你了解基于UMI和DVA构建项目的最小知识体系,你可以粗略的浏览一下本文所提到的知识,在后续的讲解中都会多次重复提起,保证学习效率.由于现在前 ...

  10. 理解JVM之JAVA运行时内存区域

    java运行时内存区域划分为方法区,堆区,虚拟机栈区,本地方法栈,程序计数器.其中方法区跟堆区是线程共享的数据区,其他的是线程私有的数据区. 1.程序计数器 程序计数器(PC)是一块较小的内存,他是存 ...