[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 2OUTPUT
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 }