bzoj3626 [ LNOI2014 ] -- 树链剖分

直接复制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),均可以完成任务。至此,题目已经被我们完美解决。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 #define N 50010
 8 #define M 201314
 9 #define ll long long
10 vector<int>g[N];
11 struct Node{
12     int f,z,d;
13     Node(){}
14     Node(int f,int z,int d):f(f),z(z),d(d){}
15 };
16 vector<Node>G[N];
17 int i,j,k,n,m,Top[N],Num,s[N],Son[N],w[N],c[N<<2],x,y,z,Ans[N],p[N<<2],f[N];
18 inline void Dfs1(int x){
19     s[x]=1;
20     for(int i=0;i<g[x].size();i++){
21         Dfs1(g[x][i]);
22         s[x]+=s[g[x][i]];
23         if(s[g[x][i]]>s[Son[x]])Son[x]=g[x][i];
24     }
25 }
26 inline void Dfs2(int x,int Tmp){
27     Top[x]=Tmp;w[x]=++Num;
28     if(Son[x])Dfs2(Son[x],Tmp);
29     for(int i=0;i<g[x].size();i++)
30     if(g[x][i]!=Son[x])Dfs2(g[x][i],g[x][i]);
31 }
32 inline void Down(int x,int y){
33     p[x<<1]+=p[x];p[x<<1|1]+=p[x];
34     c[x<<1]+=p[x]*(y+1>>1);c[x<<1|1]+=p[x]*(y>>1);
35     p[x]=0;
36 }
37 inline void Up(int x){c[x]=c[x<<1]+c[x<<1|1];}
38 inline void Update(int Node,int l,int r,int L,int R){
39     if(l>R||r<L)return;
40     if(l>=L&&r<=R){
41         c[Node]+=r-l+1;
42         p[Node]++;
43         return;
44     }
45     if(p[Node])Down(Node,r-l+1);
46     int Mid=l+r>>1;
47     Update(Node<<1,l,Mid,L,R);
48     Update(Node<<1|1,Mid+1,r,L,R);
49     Up(Node);
50 }
51 inline int Query(int Node,int l,int r,int L,int R){
52     if(l>R||r<L)return 0;
53     if(l>=L&&r<=R)return c[Node];
54     if(p[Node])Down(Node,r-l+1);
55     int Mid=l+r>>1;
56     return (Query(Node<<1,l,Mid,L,R)+Query(Node<<1|1,Mid+1,r,L,R))%M;
57 }
58 inline void Update_tree(int x){
59     while(x){
60         Update(1,1,n,w[Top[x]],w[x]);
61         x=f[Top[x]];
62     }
63 }
64 inline int Query_tree(int x){
65     int Ans=0;
66     while(x){
67         Ans=(Ans+Query(1,1,n,w[Top[x]],w[x]))%M;
68         x=f[Top[x]];
69     }
70     return Ans;
71 }
72 int main(){
73     scanf("%d%d",&n,&m);
74     for(i=2;i<=n;i++)scanf("%d",&f[i]),g[++f[i]].push_back(i);
75     Dfs1(1);Dfs2(1,1);
76     for(i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),G[x].push_back(Node(i,++z,-1)),G[y+1].push_back(Node(i,z,1));
77     for(i=1;i<=n;i++){
78         Update_tree(i);
79         for(j=0;j<G[i].size();j++){
80             Ans[G[i][j].f]+=Query_tree(G[i][j].z)*G[i][j].d;
81         }
82     }
83     for(i=1;i<=m;i++)printf("%d\n",(Ans[i]+M)%M);
84     return 0;
85 }

bzoj3626

时间: 2024-08-12 05:37:18

bzoj3626 [ LNOI2014 ] -- 树链剖分的相关文章

【树链剖分】【线段树】bzoj3626 [LNOI2014]LCA

引用题解: http://blog.csdn.net/popoqqq/article/details/38823457 题目大意: 给出一个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)].(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和) 这题看见了直接卡壳...然后

BZOJ3626 LNOI2014 LCA 树链剖分

题意:给定一棵树,每次询问给出l r z,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和 题解: 显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已标记了的点(包括自身).所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和.仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆

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 树链剖分 线段树 离线

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

[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)

[GXOI/GZOI2019]旧词 (树链剖分) 题面 给定一棵 \(n\)个点的有根树,节点标号 \([1,n]\),1号节点为根. 给定常数\(k\) 给定\(Q\)个询问,每次询问给定\(x,y\),求:\(\sum_{i=1}^x \mathrm{deep}(\mathrm{lca}(i,y)) \mod 998244353\) 分析 此题为[BZOJ3626] [LNOI2014]LCA(树链剖分)的加强版. 考虑原来的做法(k=1):我们把i到根的路径上所有点+1,y到根路径上的权值

树链剖分小结

这两周在学树剖. 先扔个模板 有一类题目,要求实现一类在树上的操作,比如: 修改/求 树上某 节点/边权 的(最)值: 修改/求 树上某 节点/边权 及其子树上所有节点的(最)值: 修改/求 树上某两点路径间的 节点/边权 的(最)值: 乍一看似乎用线段树就可以实现,但是如果仔细想想,可以发现单凭线段树是无法解决的. 对于这类题目,常用的解决方法是树链剖分. 树链剖分(节选自starszys博客): 相关定义: 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子. 轻儿子:v

树链剖分——树形到线性的转化

树链剖分: 树上操作并不能实现一段链的直接更新. 树链剖分就解决了这个问题. 本质上是树形到线性的转化. 通过子树,重链是一个连续的dfn区间的优秀性质,可以在dfn序列上进行操作,达到在树上操作的目的. 通常和线段树结合. 板子:以前写的. 树链剖分 例题: 1.遥远的国度 题目大意: 给定一棵有根树,每个点有一个权值,提供三种操作: 1.将x节点变为根节点 2.将x到y路径上的点的权值全部改为v 3.询问x的子树中点权的最小值 如果根不变,那么2.3就直接做了. 但是根变化了,随之第三问,子

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In