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 线段树 想法题的主要内容,如果未能解决你的问题,请参考以下文章

noi2017 day2t2

ZJOI2017线段树

ZJOI 2017 树状数组(线段树套线段树)

bzoj4785[Zjoi2017]树状数组 线段树套线段树

[BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)

BZOJ4785[Zjoi2017]树状数组 树套树(二维线段树)