树链剖分

hdu5111 树链剖分,主席树

≯℡__Kan透↙ 提交于 2019-11-28 03:28:13
hdu5111 链接 hdu hdu挂了,我也不知道这份代码对不对,反正过了对拍了 思路 先考虑序列上如何解决。 1 3 2 5 4 1 2 4 5 3 这个序列变成 1 2 3 4 5 1 3 5 5 2 是对答案没有影响的(显然)。 然后查询操作 \(l,r,L,R\) 就是, 一段连续的区间 \([L,R]\) 内包含几个值在 \([l,r]\) 的数字个数. 主席树就可以做了。 \(query(rt[L-1],rt[R],[l,r]的和)\) 可以用树链剖分把树上问题转化成链上。 左边一棵树树链剖分,每一条链子都是一段连续的。 右边一棵树根据父子关系建立主席树。 然后向上跳统计贡献。 错误 一遍过样例,然而清空死了。 而且这错误找了之后我也不感觉他错。 对,我感觉是c++的错。 代码 #include <iostream> #include <map> #include <cstring> #include <algorithm> #define ls(x) (t[x].ls) #define rs(x) (t[x].rs) using namespace std; const int _=1e5+7; int read() { int x=0,f=1;char s=getchar(); for(;s>'9'||s<'0';s=getchar()) if(s=='-') f

树链剖分

送分小仙女□ 提交于 2019-11-27 19:23:13
数链剖分 给一棵树,树链剖分后,树上任意一条链上的节点都可以用O(logn)个连续的区间表示。 声明: 参考课件来自 宋泽宇 (我不认识的...),讲授来自Accelerator 定义 一个点的: size 表示子树中包括改点本身的节点个数, 重儿子 表示儿子中size最大的点 一个点和它重儿子之间的边为 重边 , 除重边的其他边为轻边。 重边构成的链为 重链 (最上面的重儿子他爸也在重链上)。 性质 一个点只能在且一定在一条重链上(叶子相当于一个重链) 因为每个点只能有一个重儿子,所以两个重链不会相交 定义一个点的 top 为 该点所在重链中深度最小的点 。 步骤 首先,dfs 根节点 找 重儿子 void dfs1(int x, int fa) { a[x].deep = a[fa].deep + 1; a[x].size = 1; a[x].fa = fa; for(int i = head[x]; i; i=e[i].next ) if(e[i].y != fa) { dfs1(e[i].y , x); a[x].size += a[e[i].y ].size ; a[x].son = a[a[x].son ].size > a[e[i].y].size ? a[x].son : e[i].y ; } } 之后,再dfs一遍 找到每个点的top

树链剖分

我的未来我决定 提交于 2019-11-27 19:17:58
https://www.cnblogs.com/chinhhh/p/7965433.html#dfs1 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #define Rint register int #define mem(a,b) memset(a,(b),sizeof(a)) #define Temp template<typename T> using namespace std; typedef long long LL; Temp inline void read(T &x){ x=0;T w=1,ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); x=x*w; } #define mid ((l+r)>>1) #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r #define len (r-l+1) const int maxn=200000+10

[AHOI2005]航线规划(树链剖分+时间倒流)

China☆狼群 提交于 2019-11-27 16:50:54
传送门 练一下树剖的板子,运用一下时间倒流和下放边权的思想。 题中所谓“关键航线”其实就是桥。 删边操作桥不好维护,但如果是加边,每加一条边,两点作为端点的这条路径就都不再是桥----->考虑时间倒流。 从后往前,每删除一条边,现在就是加边,该路径上所有边都不是桥(打上标记)。 可以先求出一棵最小生成树(代码中是在dfs中实现的)那些多的边就以加边的方式加入(说明到最后一个操作后,这条路径的边也不是桥)。 #include<bits/stdc++.h> #define M 200003 #define N 60003 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } map<pair<int,int>,int>ma; int fa[N],top[N],siz[N],head[N],dep[N],vis[N],son[N],pos[N]; int sum[N<<2],mark[N<<2],n; struct EDGE{ int nextt,to,del,used; }w[M*4];

树链剖分(超详细!!!)

好久不见. 提交于 2019-11-27 16:24:34
一、轻重边剖分的过程 使用两次dfs来实现。剖分过程中要计算如下7个值: father[x]: x在树中的父亲 size[x]: x的子树结点数(子树大小) dep[x]: x在树中的深度 son[x]: x的重儿子,即为重边 top[x]: x所在重路径的顶部结点(深度最小) seg[x]: x在线段树中的位置(下标) rev[x]: 线段树中第x个位置对应的树中结点编号,即rev[seg[x]]=x 第一遍dfs时可以计算前四个值,第二遍dfs可以计算后三个值。而计算seg时,同一条重路径上的点需要按顺序排在连续的一段位置,也就是一段区间。 【代码实现】( 预处理1 ): 【代码实现】( 预处理2 ): 二、树上路径处理: 考虑将一条路径(u,v)拆分为若干条重路径:实际上就是寻找最近公共祖先的过程。考虑暴力的做法,我们会选择u,v中深度较大的点向上走一步,直到u=v。 现在有了重路径,由于我们记录了top和seg,因此我们不需要一步步走。假定top[u]和top[v]不同,那么他们的最近公共祖先可能在其中一条的重路径上,也可能在其他的重路径上,因为LCA显然不可能在top深度较大的那条重路径上,所以我们先处理top深度较大的。 首先我们找出u,v中top深度较大的点,假设是u,则我们可以直接跳到father[top[u]]处,且跳过的这一段,在线段树中是一段区间

LCA(使用树链剖分)

好久不见. 提交于 2019-11-27 13:39:34
最近刚好学了树链剖分,来一道LCA的模板题练练手: P3379 最近公共祖先(LCA) 题意:给一棵多叉树,m次询问,求两个节点之间的LCA。 思路:树链剖分。 #include <bits/stdc++.h> #include<cstring> #define L(x) (x<<1) #define R(x) (x<<1|1) #define fori(a,b,c) for(int a=b;a<=c;a++) #define ford(a,b,c) for(int a=c;a>=b;a--) #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) using namespace std; const int M=5e5+100; vector<int> g[M]; int son[M],top[M],f[M],sz[M],dep[M],tot; void dfs1(int s,int fa) { f[s]=fa; dep[s]=dep[fa]+1; sz[s]=1; int ma=-1; for(int i=0;i<g[s].size();i++) { int v=g[s][i]; if(v==fa)continue; dfs1(v,s); sz[s]+=sz[v]; if(sz[v

[动态dp][矩阵乘法][树链剖分][线段树] Luogu P4719 【模板】动态dp

给你一囗甜甜゛ 提交于 2019-11-27 13:14:28
题解 动态dp模板题,矩阵乘法有所不同C=A*B=max(a[i][j]+b[j][k]) 代码 1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 const int N=1e5+1,inf=999999999; 5 struct matrix 6 { 7 int a[3][3]; 8 void init() { for (int i=0;i<2;i++) for (int j=0;j<2;j++) a[i][j]=-inf; } 9 }ans,last,front,T[N*4],val[N]; 10 struct edge{ int to,from; }e[N*2]; 11 struct Tree{ int size,id,fa,top,son,deep,end; }t[N*2]; 12 int n,m,cnt,tot,ldp[N][2],dp[N][2],head[N],a[N],b[N]; 13 void insert(int x,int y) 14 { 15 e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; 16 e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt; 17 } 18 matrix mul

树链剖分详解

耗尽温柔 提交于 2019-11-27 10:22:04
树链剖分,顾名思义,就是对树剖分成链,然后用数据结构进行维护,以此降低维护的复杂度。 必备知识点 邻接表存图 LCA 线段树 相关定义 重儿子:一个节点所有子节点中以其为根的子树的节点最多的节点 重边:一个节点到其重儿子的边 重链:一条全部由重边构成的路径(特别地,一个节点也当做一条重链) 轻儿子:一个节点除重儿子外的所有子节点 轻边:一个节点到其轻儿子的边 节点信息 $fa[x]$:$x$的父亲节点 ​$dep[x]$:$x$的深度 ​$siz[x]$:以$x$的子树的节点数 $son[x]$:$x$的重儿子 ​$top[x]$:$x$所在重链的顶部节点(即深度最小的节点) ​$seg[x]$:$x$在线段树中的节点编号 ​$rev[x]$:线段树中编号为$x$的节点在原树中对应的节点编号 性质 若(x,y)为轻边,则$siz(y)\leq siz(u)/2$。 证明:显然,若$siz(y)>siz(u)/2$,以y为根的子树的节点个数一定是u的儿子中最多的,与$(x,y)$是轻边,即y是轻儿子矛盾。因此$siz(y)\leq siz(u)/2$。 从根节点到树上任意一点的路径上的轻边数不超过$logn$。 证明:由上一性质可知,从根节点向下走,每经过一条轻边,以到达的节点为根的子树的节点个数至少比以上一个节点为根的子树减少一半,因此从根节点到树上任意一点的路径上的轻边数不超过

树链剖分模板题

末鹿安然 提交于 2019-11-27 09:31:03
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入格式 第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。 接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。 接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通) 接下来M行每行包含若干个正整数,每行表示一个操作,格式如下: 操作1: 1 x y z 操作2: 2 x y 操作3: 3 x z 操作4: 4 x 输出格式 输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模) 输入输出样例 输入 #1 复制 5 5 2 24 7 3 7 8 0 1 2 1 5 3 1 4 1 3 4 2 3 2 2 4 5 1 5 1 3 2 1 3 输出 #1 复制 2 21 #include <bits/stdc++.h> using namespace std;

树状数组树链剖分

不羁岁月 提交于 2019-11-27 03:32:53
其实树状数组和线段树写树剖都差不多,只是换了一种储存数据的方式,一种占用空间小,但是相对耗时,一种占用空间大,但是很快。 模板题: 树链剖分 用树状数组会 tle 但是这也是一种思路 // Created by CAD on 2019/8/11. #include <bits/stdc++.h> using namespace std; using ll=long long; #define lowbit(i) (i&-i) const int maxn=1e5+5; /*树状数组*/ int c[maxn], wt[maxn], laz[maxn << 2]; ll n; ll mod; inline void Add(int x, int k) { while (x<=n) c[x]=(c[x]+k)%mod, x+=lowbit(x); } inline ll getSum(int x) { ll ans=0; while (x>0) ans=(ans+c[x])%mod, x-=lowbit(x); return ans%mod; } inline ll getsum(int l, int r, int s, int t, int p) { return (getSum(r)-getSum(l-1)+mod)%mod; } int cnt=0, head[maxn << 1