【LNOI2014】【BZOJ3626】NOIp2018模拟(三) LCA
Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设$dep[i]$表示点i的深度,$lca(i,j)$表示i与j的最近公共祖先。
有q次询问,每次询问给出l,r,z,求$\sum\limits_{i=l}^{r}dep[lca(i,z)]$。
(即求在$[l,r]$区间内的每个节点i与z的最近公共祖先的深度之和)
$n,q<=50000$
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
Sample Output
8
5
这题不算很难,但是看到正解的方法很有趣,就记录一下~
考虑暴力,每次询问把z到根的所有节点打上标记,枚举i的时候直接往根找第一个有标记的点,然后统计深度即可。
然而复杂度明显是大于$O(n^2q)$的,显然不能接受,容易发现如果把深度看成点权,那么统计深度就相当于把z到根的每个节点权值都加一,然后枚举时统计根节点到i的权值和。使用树链剖分可以降到$O(nqlog^2n)$,但是还是会超时。
考虑进一步优化,发现询问可以差分,拆成$[1,l-1]$和$[1,r]$两个询问,进一步可以发现对答案有贡献的点只会在$lca(i,z)$以上,因此把每个i到根路径上的结点权值加一,再统计z到根节点的权值和,得出的答案是相同的。再结合差分,每次直接将$[1,l-1]$或$[1,r]$中所有节点到根节点路径上的点权值加一,然后统计z到根节点的权值和,按照dfs序区间修改+区间查询,用树链剖分加线段树可以做到$O(qlog^2n)$,用LCT可以做到$O(qlogn)$
注意long long
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #define mod 201314 7 using namespace std; 8 typedef long long ll; 9 struct edge{ 10 int v,next; 11 }a[200001]; 12 struct task{ 13 int r,z,id,ok; 14 }q[200001]; 15 int n,qq,u,v,z,tmp=0,qqq=0,tot=0,tim=0,head[100001],son[100001],siz[100001],fa[100001],dep[100001],dfn[100001],top[100001]; 16 ll t[500001],laz[500001],tv[500001],ans[100001],anss; 17 bool cmp(task a,task b){ 18 return a.r<b.r; 19 } 20 void add(int u,int v){ 21 a[++tot].v=v; 22 a[tot].next=head[u]; 23 head[u]=tot; 24 } 25 void dfs1(int u,int f,int dpt){ 26 dep[u]=dpt; 27 fa[u]=f; 28 siz[u]=1; 29 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 30 int v=a[tmp].v; 31 if(!dep[v]){ 32 dfs1(v,u,dpt+1); 33 siz[u]+=siz[v]; 34 if(siz[v]>siz[son[u]]||son[u]==-1)son[u]=v; 35 } 36 } 37 } 38 void dfs2(int u,int tp){ 39 dfn[u]=++tim; 40 top[u]=tp; 41 if(son[u]!=-1)dfs2(son[u],tp); 42 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 43 int v=a[tmp].v; 44 if(v!=son[u])dfs2(v,v); 45 } 46 } 47 void pushup(int u){ 48 t[u]=t[u*2]+t[u*2+1]; 49 } 50 void pushdown(int u){ 51 if(laz[u]){ 52 laz[u*2]+=laz[u]; 53 laz[u*2+1]+=laz[u]; 54 t[u*2]+=tv[u*2]*laz[u]; 55 t[u*2+1]+=tv[u*2+1]*laz[u]; 56 laz[u]=0; 57 } 58 } 59 void build(int l,int r,int u){ 60 if(l==r){ 61 tv[u]=1; 62 return; 63 } 64 int mid=(l+r)/2; 65 build(l,mid,u*2); 66 build(mid+1,r,u*2+1); 67 tv[u]=tv[u*2]+tv[u*2+1]; 68 } 69 void updata(int l,int r,int u,int L,int R,int v){ 70 if(L<=l&&r<=R){ 71 t[u]+=(ll)tv[u]*v; 72 laz[u]+=v; 73 return; 74 } 75 int mid=(l+r)/2; 76 pushdown(u); 77 if(L<=mid)updata(l,mid,u*2,L,R,v); 78 if(mid<R)updata(mid+1,r,u*2+1,L,R,v); 79 pushup(u); 80 } 81 ll query(int l,int r,int u,int L,int R){ 82 if(L<=l&&r<=R){ 83 return t[u]; 84 } 85 int mid=(l+r)/2,ans=0; 86 pushdown(u); 87 if(L<=mid)ans+=query(l,mid,u*2,L,R); 88 if(R>mid)ans+=query(mid+1,r,u*2+1,L,R); 89 return ans; 90 } 91 void work(int u){ 92 while(u){ 93 int v=top[u]; 94 updata(1,n,1,dfn[v],dfn[u],1); 95 u=fa[v]; 96 } 97 } 98 ll _work(int u){ 99 ll ans=0; 100 while(u){ 101 int v=top[u]; 102 ans+=query(1,n,1,dfn[v],dfn[u]); 103 u=fa[v]; 104 } 105 return ans; 106 } 107 int main(){ 108 memset(son,-1,sizeof(son)); 109 memset(head,-1,sizeof(head)); 110 scanf("%d%d",&n,&qq); 111 for(int i=1;i<n;i++){ 112 scanf("%d",&u); 113 add(u+1,i+1); 114 } 115 dfs1(1,0,1); 116 dfs2(1,1); 117 build(1,n,1); 118 for(int i=1;i<=qq;i++){ 119 scanf("%d%d%d",&u,&v,&z); 120 u++,v++,z++; 121 q[++qqq]=(task){u-1,z,i,-1}; 122 q[++qqq]=(task){v,z,i,1}; 123 } 124 sort(q+1,q+qqq+1,cmp); 125 for(int i=1;i<=qqq;i++){ 126 while(tmp<q[i].r){ 127 work(++tmp); 128 } 129 ans[q[i].id]+=(ll)q[i].ok*_work(q[i].z); 130 } 131 for(int i=1;i<=qq;i++){ 132 printf("%lld\n",ans[i]%mod); 133 } 134 return 0; 135 }