bzoj 3626

链剖,居然还可以这样求LCA,学习了orz

直接引用清华爷gconeice的题解吧

显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r
之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z
到根的路径上的点全部 +1,对于 l 到 r
之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点
i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1,
r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i
到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT
均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log
n),均可以完成任务。至此,题目已经被我们完美解决。

吐槽一下。。pushdown居然连续写错2个,差一个就要半天QAQ

  1 #include<bits/stdc++.h>
  2 #define inc(i,l,r) for(int i=l;i<=r;i++)
  3 #define dec(i,l,r) for(int i=l;i>=r;i--)
  4 #define link(x) for(edge *j=h[x];j;j=j->next)
  5 #define mem(a) memset(a,0,sizeof(a))
  6 #define inf 201314
  7 #define ll long long
  8 #define succ(x) (1<<x)
  9 #define NM 50000+5
 10 using namespace std;
 11 int read(){
 12     int x=0,f=1;char ch=getchar();
 13     while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
 14     while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
 15     return x*f;
 16 }
 17 struct info{
 18     ll z,s,size;
 19 }T[8*NM],q[NM],null;
 20 info operator+(const info&x,const info&y){
 21     info f;
 22     f.s=x.s+y.s;
 23     f.size=x.size+y.size;
 24     f.z=0;
 25     return f;
 26 }
 27 struct tmp{
 28     int t,x,z;
 29 }a[2*NM];
 30 bool cmp(tmp x,tmp y){
 31     return x.x<y.x;
 32 }
 33 struct edge{
 34     int t,v;
 35     edge *next;
 36 }e[NM],*h[NM],*p=e;
 37 void add(int x,int y){
 38     p->t=y;p->next=h[x];h[x]=p;p++;
 39 }
 40 int d[NM],f[NM],size[NM],son[NM],id[NM],_id[NM],top[NM],TOP,tot;
 41 int num,n,m,_x,_y,l,r,t;
 42 void dfs1(int x){
 43     link(x)
 44     if(!f[j->t]){
 45         f[j->t]=x;
 46         d[j->t]=d[x]+1;
 47         dfs1(j->t);
 48         size[x]+=size[j->t];
 49         if(size[j->t]>size[son[x]])son[x]=j->t;
 50     }
 51     size[x]++;
 52 }
 53 void dfs2(int x){
 54     top[x]=TOP;id[x]=++tot;_id[tot]=x;
 55     if(son[x])dfs2(son[x]);
 56     link(x)
 57     if(!top[j->t])dfs2(TOP=j->t);
 58 }
 59 void pushdown(int i){
 60     if(T[i].z){
 61         T[i<<1].s+=T[i].z*T[i<<1].size;
 62         T[i<<1|1].s+=T[i].z*T[i<<1|1].size;
 63         T[i<<1].z+=T[i].z;
 64         T[i<<1|1].z+=T[i].z;
 65         T[i].z=0;
 66     }
 67 }
 68 void build(int i,int x,int y){
 69     int t=x+y>>1;
 70     if(x==y){
 71         T[i].size=1;
 72         return;
 73     }
 74     build(i<<1,x,t);build(i<<1|1,t+1,y);
 75     T[i]=T[i<<1]+T[i<<1|1];
 76 }
 77 void ch(int i,int x,int y){
 78     int t=x+y>>1;
 79     if(l<=x&&y<=r){
 80         T[i].z+=1;T[i].s+=T[i].size;
 81         return;
 82     }
 83     if(y<l||r<x)return;
 84     pushdown(i);
 85     ch(i<<1,x,t);ch(i<<1|1,t+1,y);
 86     T[i]=T[i<<1]+T[i<<1|1];
 87 }
 88 void update(int x){
 89     while(top[x]!=1){
 90         l=id[top[x]];r=id[x];
 91         ch(1,1,n);
 92         x=f[top[x]];
 93     }
 94     l=id[1];r=id[x];
 95     ch(1,1,n);
 96 }
 97 info query(int i,int x,int y){
 98     int t=x+y>>1;
 99     pushdown(i);
100     if(l<=x&&y<=r)return T[i];
101     if(y<l||r<x)return null;
102     return query(i<<1,x,t)+query(i<<1|1,t+1,y);
103 }
104 int _query(int x){
105     int s=0;
106     while(top[x]!=1){
107         l=id[top[x]];r=id[x];
108         s+=query(1,1,n).s;
109         x=f[top[x]];
110     }
111     l=id[1];r=id[x];
112     s+=query(1,1,n).s;
113     return s;
114 }
115 int main(){
116     n=read();m=read();
117     inc(i,2,n){
118         _x=read()+1;
119         add(_x,i);
120     }
121     f[1]=1;null.s=0;
122     dfs1(1);
123     dfs2(TOP=1);
124     build(1,1,n);
125     inc(i,1,m){
126         _x=read();_y=read()+1;
127         q[i].z=read()+1;
128         a[++num].x=_x;a[num].t=i;a[num].z=1;
129         a[++num].x=_y;a[num].t=i;a[num].z=0;
130     }
131     sort(a+1,a+num+1,cmp);
132     t=0;
133     inc(i,1,num){
134         while(a[i].x>t){
135             t++;
136             update(t);
137 //            printf("%d\n",T[1].s);
138         }
139         _x=a[i].t;
140         if(a[i].z==1)q[_x].s-=_query(q[_x].z);
141         else q[_x].s+=_query(q[_x].z);
142     }
143     inc(i,1,m)printf("%d\n",q[i].s%inf);
144     return 0;
145 }

时间: 2024-08-15 02:04:46

bzoj 3626的相关文章

[BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】

题目链接: BZOJ - 3626 题目分析 考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和. 这个方法是可以叠加的,这是非常有用的一点.如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和. 不仅满足可加性,还满足可减

BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LC

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

bzoj 3626: [LNOI2014]LCA 离线+树链剖分

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 426  Solved: 124[Submit][Status] Description 给出一个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)].(即

【BZOJ 3626】 [LNOI2014]LCA

3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MB Submit: 735 Solved: 250 [Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[L

BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题,是时候复习一波离线算法泡脑子了.(没有暴力分的题,想不出来正解就爆零,太可怕了) 排序后离线操作通过前缀和计算答案,题解是hzwer的博客上复制的 http://hzwer.com/3891.html 直接引用清华爷gconeice的题解吧 显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力

BZOJ 3626 LNOI 2014 LCA 树链剖分

题目大意:给出一棵树,有n个问题,询问在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和. 思路:不会,然后看了题解,之后发现自己智商严重不足. 看到数据范围就知道一定要离线处理,就这个离线处理我估计以我的智商不看题解是肯定想不出来的.. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已标记了的点(包括自身).所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r

bzoj 3626 LCA

这一道题咋一看只觉得是离线,可以求出所有的f(1,i,z), 答案就等于f(1,r,z)-f(1,l-1,z).但是没有具体的做法,但是求LCA的深度和有一个非常巧妙的做法,每加一个点,就把这个点到根的路径上的点权值+1,这样计算某个点和之前所有点LCA深度和就可以统计这个点到根的路径上的点的权值和.这样就可以用树链剖分很快的修改和得出答案,这题就解决了. 上代码: #include <cstdio> #include <cstring> #include <cstdlib&

BZOJ 3626 LCA(离线+树链剖分)

首先注意到这样一个事实. 树上两个点(u,v)的LCA的深度,可以转化为先将u到根路径点权都加1,然后求v到根路径上的总点权值. 并且该题支持离线.那么我们可以把一个区间询问拆成两个前缀和形式的询问. 现在问题就变成了求[1,r]和x的LCA深度之和.实际上就是把[1,r]到根路径点权点1,然后求x到根路径上的总权值. 我们按编号从小往大依次加路径点权.然后就可以有序处理询问.用树链剖分维护的话,总复杂度为O((n+q)lognlogn).