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 }
时间: 2025-01-13 21:56:40

ZJOI2017 day2 T2 线段树 想法题的相关文章

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4027 Can you answer these queries? 线段树裸题

题意: 给定2个操作 0.把区间的每个数sqrt 2.求和 因为每个数的sqrt次数很少,所以直接更新到底,用个标记表示是否更新完全(即区间内的数字只有0,1就不用再更新了) #include<stdio.h> #include<iostream> #include<algorithm> #include<vector> #include<cmath> #include<queue> #include<set> #incl

[POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]

可持久化线段树模板题. #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using namespace std; int n,q,tot,a[110000]; in

POJ 3468 线段树裸题

这些天一直在看线段树,因为临近期末,所以看得断断续续,弄得有些知识点没能理解得很透切,但我也知道不能钻牛角尖,所以配合着刷题来加深理解. 然后,这是线段树裸题,而且是最简单的区间增加与查询,我参考了ACdreamer的模板,在此基础上自己用宏定义来精简了一下代码: 1 #include<cstdio> 2 typedef long long LL; 3 #define root int rt, int l, int r 4 #define lson rt*2, l, mid 5 #define

[ACM] Color the ball [线段树水题][数组开大]

Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色.但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗? Input 每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N).  当N

poj 2182 Lost Cows(线段树经典题)

题目链接:http://poj.org/problem?id=2182 Lost Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9152   Accepted: 5879 Description N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, th

LA 2191电位计(线段树模板题)

线段树模板题,没啥好说的.....注意输出是case之间空一行就行.........之前一直没注意,一直wa 代码如下: #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #includ

【整理】线段树30题

1,poj 1151 Atlantis: 求矩形面积并. 2,poj 1177 Picture: 求矩形轮廓的周长. 3,poj 1389 Area of Simple Polygons :同第一题. 4,poj 1823 Hotel :线段树线段的插入删除求线段树中最长的线段长度 5,poj 2104 K-th Number:线段树维护归并排序树+三次二分查找   (区间第k大 ,主席树也行,前者可以练习试一下). 6,poj 2155 Matrix :求二维平面的矩形信息,二维线段树,或者二

Codeforces Round #393 (Div. 2) (8VC Venture Cup 2017 - Final Round Div. 2 Edition) E - Nikita and stack 线段树好题

http://codeforces.com/contest/760/problem/E 题目大意:现在对栈有m个操作,但是顺序是乱的,现在每输入一个操作要求你输出当前的栈顶, 注意,已有操作要按它们的时间顺序进行. 思路:线段树好题啊啊,我们把push当成+1, pop当成-1,按操作的位置建立线段树,那么如何 寻找栈顶呢,我们计算每个点的后缀,栈顶就是下标最大的>0的后缀,我们拿后缀建立线段树, 剩下的就是区间加减法,和求区间最大值啦. #include<bits/stdc++.h>