ZJOI2017 day2 T2 线段树 想法题
Posted 汪立超
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZJOI2017 day2 T2 线段树 想法题相关的知识,希望对你有一定的参考价值。
考完D2发现自己简直zz了。。。花式扔基本分
首先这道题有个显然的套路:树上一些点到一个定点的距离和=这些点深度和+点数*定点深度和-2*lca深度和
——上一次见这个套路是LNOI2014,上次做的时候还比较naive:http://www.cnblogs.com/wanglichao/p/6425893.html
这次考场上也只想到这一步了,,并没有发现广义线段树的奇特性质
奇特性质:被选中的从左到右一定是一串右儿子和一串左儿子,而且都是挂在l-1到r+1上的连续右(左)儿子
这么一来,一个询问可以分成两部分,这两部分都可以O(n)预处理出来
在预处理的时候考虑维护两个东西:从根节点到当前点的链上所直接挂的所有右儿子的个数(记为sum[i])和深度和(sumd[i])
那么在统计的时候 这些点深度和+点数*定点深度和-2*lca深度和 可以轻松算出
(一脸懵逼.jpg)
Q:如何算lca深度和?
A:分类讨论,计算询问的定点挂到链上是哪里(以下称为悬挂点x)
①悬挂点以上的点与定点的lca深度为自己深度-1,所以总和为“sumd[x]-sum[x]”
②悬挂点的右儿子如果被算在答案里,那么深度就是"dep[x]+1"
③悬挂点以下的点与定点的lca深度一定为dep[x],总和为"(sum[l-1]-sum[x])*dep[x]"
求和即可(注意细节)
左儿子同理
Q:l=1或者r=n怎么办
A:只算半边(自行理解,不可言传)
上个代码冷静一下(目前uoj上最短榜第一来自这个代码微改@wzf2000):
1 #include <bits/stdc++.h> 2 #define bel(x,y) (L[x]>=L[y] && R[x]<=R[y]) 3 using namespace std; 4 long long n,N,IN,m,u,l,r,LOG; 5 long long mer[800001],ls[800001],rs[800001],sum[800001][2],dep[800001],sd[800001][2]; 6 long long fa[800001][20]; 7 long long L[800001],R[800001],nod[800001]; 8 long long lca(long long a,long long b) 9 { 10 if(bel(a,b)) return b; 11 if(bel(b,a)) return a; 12 long long now=a; 13 for(long long i=LOG;i>=0;i--) 14 if(fa[now][i] && !bel(b,fa[now][i])) now=fa[now][i]; 15 return fa[now][0]; 16 } 17 void Dfs(long long now) 18 { 19 if(!ls[now]) return; 20 sum[rs[now]][0]=sum[now][0]; 21 sum[rs[now]][1]=sum[now][1]+1; 22 sum[ls[now]][0]=sum[now][0]+1; 23 sum[ls[now]][1]=sum[now][1]; 24 dep[ls[now]]=dep[now]+1; 25 dep[rs[now]]=dep[now]+1; 26 sd[rs[now]][0]=sd[now][0]; 27 sd[rs[now]][1]=sd[now][1]+dep[now]+1; 28 sd[ls[now]][0]=sd[now][0]+dep[now]+1; 29 sd[ls[now]][1]=sd[now][1]; 30 Dfs(ls[now]); 31 Dfs(rs[now]); 32 } 33 long long dfs(long long l,long long r) 34 { 35 long long now=++N; 36 L[now]=l;R[now]=r; 37 for(long long i=0;fa[fa[now][i]][i];i++) fa[now][i+1]=fa[fa[now][i]][i]; 38 if(l==r) return nod[l]=now; 39 long long me=mer[IN++]; 40 fa[N+1][0]=now; 41 ls[now]=dfs(l,me); 42 fa[N+1][0]=now; 43 rs[now]=dfs(me+1,r); 44 return now; 45 } 46 long long getl(long long l) 47 { 48 long long lc=lca(l,u); 49 long long now=dep[lc]*(sum[l][0]-sum[lc][0])+(bel(l,ls[lc]) && lc!=u)+sd[lc][0]-sum[lc][0]; 50 long long ans=sd[l][0]+sum[l][0]*dep[u]-now*2; 51 return ans; 52 } 53 long long getr(long long r) 54 { 55 long long lc=lca(r,u); 56 long long now=dep[lc]*(sum[r][1]-sum[lc][1])+(bel(r,rs[lc]) && lc!=u)+sd[lc][1]-sum[lc][1]; 57 long long ans=sd[r][1]+sum[r][1]*dep[u]-now*2; 58 return ans; 59 } 60 int main() 61 { 62 scanf("%d",&n); 63 for(long long i=1;i<=n;i<<=1) 64 LOG++; 65 for(long long i=1;i<n;i++) 66 scanf("%d",&mer[i]); 67 IN=1; 68 dfs(1,n); 69 Dfs(1); 70 scanf("%d",&m); 71 for(long long i=1;i<=m;i++) 72 { 73 scanf("%d%d%d",&u,&l,&r); 74 --l;++r; 75 if(!l && r>n) 76 { 77 printf("%d\\n",dep[u]); 78 continue; 79 } 80 long long ans=0; 81 if(l) 82 ans+=getl(nod[l])-((r<=n)?getl(ls[lca(nod[l],nod[r])]):0); 83 if(r<=n) 84 ans+=getr(nod[r])-(l?getr(rs[lca(nod[l],nod[r])]):0); 85 printf("%lld\\n",ans); 86 } 87 return 0; 88 }
以上是关于ZJOI2017 day2 T2 线段树 想法题的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4785[Zjoi2017]树状数组 线段树套线段树