[LNOI2014]LCA

[LNOI2014]LCA

题目

给出一个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

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

OUTPUT

8

5

解题报告

首先提出一个结论

$x$点与$y$点的$LCA$可以由如下方式求得:

将$x$点到根路径上所有点点权置为$1$,其余点点权置为$0$,则$y$点到根路径上点权和即为所求

原因很简单,手画一下就可以发现这个规律

有了这个结论,我们就可以把求$LCA$这种操作转化为路径上的点权操作了,也就是说,树链剖分就可行了起来

我们考虑如何处理一段区间的贡献,显然可以用差分来搞,那么我们就可以将操作离线,将询问拆成两个,进行差分,扫一遍节点,进行点权覆盖与差分查询即可

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <vector>
 5 using namespace std;
 6 inline int read(){
 7     int sum(0);char ch(getchar());
 8     for(;ch<‘0‘||ch>‘9‘;ch=getchar());
 9     for(;ch>=‘0‘&&ch<=‘9‘;sum=sum*10+(ch^48),ch=getchar());
10     return sum;
11 }
12 const int mod(201314);
13 struct edge{int e;edge *n;}*pre[50005];
14 inline void insert(int s,int e){edge *tmp(new edge);tmp->e=e;tmp->n=pre[s];pre[s]=tmp;}
15 int n,q;
16 int z[50005];
17 vector<int>sub[50005],add[50005];
18 int sum[200005],lazy[200005];
19 inline void pushup(int i){sum[i]=(sum[i<<1]+sum[i<<1|1])%mod;}
20 inline void pushdown(int i,int len){
21     if(lazy[i]){
22         lazy[i<<1]=(lazy[i<<1]+lazy[i])%mod;
23         lazy[i<<1|1]=(lazy[i<<1|1]+lazy[i])%mod;
24         sum[i<<1]=(sum[i<<1]+lazy[i]*(len-(len>>1))%mod)%mod;
25         sum[i<<1|1]=(sum[i<<1|1]+lazy[i]*(len>>1)%mod)%mod;
26         lazy[i]=0;
27     }
28 }
29 inline void update(int ll,int rr,int w,int l,int r,int i){
30     if(ll<=l&&r<=rr){lazy[i]=(lazy[i]+w)%mod;sum[i]=(sum[i]+w*(r-l+1)%mod)%mod;return;}
31     int mid((l+r)>>1);pushdown(i,r-l+1);
32     if(ll<=mid)update(ll,rr,w,l,mid,i<<1);if(mid<rr)update(ll,rr,w,mid+1,r,i<<1|1);pushup(i);
33 }
34 inline int query(int ll,int rr,int l,int r,int i){
35     if(ll<=l&&r<=rr)return sum[i];int ret(0),mid((l+r)>>1);pushdown(i,r-l+1);
36     if(ll<=mid)ret=query(ll,rr,l,mid,i<<1);if(mid<rr)ret=(ret+query(ll,rr,mid+1,r,i<<1|1))%mod;return ret;
37 }
38 int ans[50005];
39 int fa[50005],son[50005],size[50005],dep[50005];
40 inline void dfs1(int u){
41     son[u]=0;size[u]=1;
42     for(edge *i=pre[u];i;i=i->n){
43         int e(i->e);dep[e]=dep[u]+1;dfs1(e);
44         size[u]+=size[e];if(size[e]>size[son[u]])son[u]=e;
45     }
46 }
47 int cnt,id[50005],pos[50005],top[50005];
48 inline void dfs2(int u,int rt){
49     top[u]=rt;id[u]=++cnt;pos[cnt]=u;if(son[u])dfs2(son[u],rt);
50     for(edge *i=pre[u];i;i=i->n){int e(i->e);if(e==son[u])continue;dfs2(e,e);}
51 }
52 inline void change(int x,int y){
53     while(top[x]^top[y]){
54         if(dep[top[x]]<dep[top[y]])swap(x,y);
55         update(id[top[x]],id[x],1,1,n,1);
56         x=fa[top[x]];
57     }
58     if(dep[x]>dep[y])swap(x,y);
59     update(id[x],id[y],1,1,n,1);
60 }
61 inline int ask(int x,int y){
62     int ret(0);
63     while(top[x]^top[y]){
64         if(dep[top[x]]<dep[top[y]])swap(x,y);
65         ret=(ret+query(id[top[x]],id[x],1,n,1))%mod;
66         x=fa[top[x]];
67     }
68     if(dep[x]>dep[y])swap(x,y);
69     ret=(ret+query(id[x],id[y],1,n,1))%mod;
70     return ret;
71 }
72 int main(){
73     n=read();q=read();
74     for(int i=2;i<=n;++i){
75         int x(read()+1);fa[i]=x;insert(x,i);
76     }
77     for(int i=1;i<=q;++i){
78         int x(read()+1),y(read()+1);z[i]=read()+1;
79         sub[x-1].push_back(i);add[y].push_back(i);
80     }
81     dfs1(1);dfs2(1,1);
82     for(int i=1;i<=n;++i){
83         change(i,1);
84         for(int j=0,k=sub[i].size();j<k;++j)
85             ans[sub[i][j]]=((ans[sub[i][j]]-ask(z[sub[i][j]],1))%mod+mod)%mod;
86         for(int j=0,k=add[i].size();j<k;++j)
87             ans[add[i][j]]=(ans[add[i][j]]+ask(z[add[i][j]],1))%mod;
88     }
89     for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
90 }

时间: 2024-08-30 10:56:51

[LNOI2014]LCA的相关文章

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

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LC

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

bzoj 3626: [LNOI2014]LCA 离线+树链剖分

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 426  Solved: 124[Submit][Status] 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)].(即

【BZOJ 3626】 [LNOI2014]LCA

3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MB Submit: 735 Solved: 250 [Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[L

【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树

[BZOJ3626][LNOI2014]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的父节点编号.接

Bzoj3626 [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 2007  Solved: 800 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的最近公共祖先的深度之和) I

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

引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个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的最近公共祖先的深度之和) 这题看见了直接卡壳...然后

[BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】

题目链接: BZOJ - 3626 题目分析 考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和. 这个方法是可以叠加的,这是非常有用的一点.如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和. 不仅满足可加性,还满足可减

BZOJ 3626 [LNOI2014]LCA

一道比较神奇的题. 树链剖分+奇技淫巧: 神奇地发现,把z到跟的路径上的点值+1,查询一个点到跟的路径和就是它与z的lca的深度. 相对的,把l~r到跟的路径上的点值+1,查询z到跟的路径和就是要的答案. 考虑差分,把一个询问拆成两个,把所有询问排序然后从0~n-1到跟路径上的值+1: 一开始狂WA,发现把线段树区间加的(l-r)*v打成了(qr-ql)*v了... //Twenty #include<cstdio> #include<cstdlib> #include<io