经常听到大佬说树剖云云,大概学一下。
-
树链剖分
树剖通过将整棵树划分为多条重链,将其转换为线性结构,方便我们使用其他数据结构维护这个树的信息。
概念:
重儿子:对于每一个非叶子节点,它的儿子中子树节点数最大的儿子,为该节点的重儿子
轻儿子:对于每一个非叶子节点,它的儿子中重儿子以外的所有儿子为轻儿子
叶子节点没有重儿子也没有轻儿子(因为它没有儿子。。)
重边:一个父亲连接他的重儿子的边称为重边
轻边:剩下的即为轻边
重链:相邻重边连起来的 连接一条重儿子 的链叫重链 (对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链,每一条重链以轻儿子为起点)
重边:一个父亲连接他的重儿子的边称为重边
轻边:剩下的即为轻边
重链:相邻重边连起来的 连接一条重儿子 的链叫重链 (对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链,每一条重链以轻儿子为起点)
////偷来的图/////
可以看到重新编号之后的树中,每条链的编号是连续的,每个子树的编号也是连续的,那么对于子树上的操作和查询,就可以通过线段树对区间的维护实现。
DFS大致流程:
dfs1处理:
- 以每个点为根的子树节点数size
- 每个点的重儿子son (通过比较节点数可以很方便实现)
- 每个结点的父亲fa
- 每个结点的深度deep(对优化很重要)
dfs2处理:
- 为每个结点重新编号(如果要结合一些数据结构的话)
- 为新编好的结点赋值(再开一个数组作为数据数组)
- 获得每个点所在链的结点编号top
(这里注意dfs2的时候对儿子先重后轻处理,这样每个链和每个子树的编号才能连续)
-
树链剖分与LCA
用树链求LCA思想与倍增很像
对a,b要求其lca,从当前节点中链头深度大的一点开始向上跳,每次跳到当前结点所在链的顶点的父节点
当两点在同一条链时退出循环,返回深度较小的点
代更。
当两点在同一条链时退出循环,返回深度较小的点
代更。
洛谷链接:https://www.luogu.org/problem/P3398
#include<bits/stdc++.h> using namespace std; #define C getchar() typedef long long ll; #define pi acos(-1.0) #define INF 0x3f3f3f3f #define mod 1000000007 const int MAXN = 1e5 + 10; #define pii pair<int, int> #define endll printf("\n") #define stop system("pause") #define lson rt << 1, l, mid #define lowbit(x) ((x) & (-x)) #define rson rt * 2 + 1, mid + 1, r #define FOR(i,a,b) for(int i=a;i<=b;i++) #define mem(a, b) memset(a, b, sizeof(a)) inline void rd(int &s) { s = 0;int t = 1, k = C; for (; k < '0' || k > '9'; k = C)if (k == '-') t = -1; for (; k >= '0' && k <= '9'; k = C) s = (s << 1) + (s << 3) + (k ^ 48); s *= t; } int n,q; vector<int>g[MAXN]; //dfs1 int son[MAXN],fa[MAXN]; int dep[MAXN],size[MAXN]; //dfs2 int top[MAXN]; inline void dfs1(int u,int pre,int deep) { size[u]=1;dep[u]=deep;fa[u]=pre; int maxson=-1; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(v==pre) continue; dfs1(v,u,deep+1); size[u]+=size[v]; if(size[v]>maxson) maxson=size[v],son[u]=v; } } inline void dfs2(int x,int topx)// { top[x]=topx; if(son[x]==-1) return ; dfs2(son[x],topx); for(int i=0;i<g[x].size();i++) { int v=g[x][i]; if(v==fa[x]||v==son[x]) continue; dfs2(v,v); } } int lca(int x,int y) { for(;top[x]!=top[y];x=fa[top[x]]) if(dep[top[x]]<dep[top[y]]) swap(x,y);// if(dep[x]>dep[y]) swap(x,y); return x; } int main() { rd(n);rd(q); FOR(i,1,n-1) { int u,v;rd(u);rd(v); g[u].push_back(v); g[v].push_back(u); } // mem(son,-1); dfs1(1,0,1); dfs2(1,0); // while(q--) { int a,b,c,d; rd(a),rd(b),rd(c),rd(d); int s=lca(a,b),t=lca(c,d); if(dep[s]<dep[t]) swap(s,t),swap(a,c),swap(b,d); if(lca(s,c)==s||lca(s,d)==s) printf("Y\n"); else printf("N\n"); } //stop; return 0; }
树链剖分与线段树
今天写动归写不下去。。只好来写树剖
链接:https://www.luogu.org/problem/P4092
利用树链维护子树信息时,一个节点的子树即为id[x]~id[x]+size[x]-1的区间
操作1:把某点值变为1 ,线段树维护值为1的最大编号,初始值为1 。更改某点时,更改该点的子树那部分的区间最大值。
操作2:查询到x~1路径上值为1的编号最大的原编号 ,利用树剖+线段树在向上跳的时候求出最大值
一开始开小了RE了,,其他就是板子???
太好了丰富了我的板子)
#include<bits/stdc++.h> using namespace std; #define C getchar() typedef long long ll; #define pi acos(-1.0) #define INF 0x3f3f3f3f #define mod 1000000007 const int MAXN = 1e5 + 10; #define pii pair<int, int> #define endll printf("\n") #define stop system("pause") #define lowbit(x) ((x) & (-x)) #define Temp template<typename T> #define FOR(i,a,b) for(int i=a;i<=b;i++) #define mem(a, b) memset(a, b, sizeof(a)) Temp inline void rd(T &s) { s = 0;T t = 1, k = C; for (; k < '0' || k > '9'; k = C)if (k == '-') t = -1; for (; k >= '0' && k <= '9'; k = C) s = (s << 1) + (s << 3) + (k ^ 48); s *= t; } int n; vector<int>g[MAXN]; //dfs1 int son[MAXN],fa[MAXN]; int dep[MAXN],size[MAXN]; //dfs2 int top[MAXN],id[MAXN],rk[MAXN]; //tree int cnt; struct Segment_Tree { #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r int maxv[MAXN<<2];int lazy[MAXN<<2]; inline int ls(int p) {return p<<1;} inline int rs(int p) {return p<<1|1;} void push_up(int p) {maxv[p] = max(maxv[ls(p)],maxv[rs(p)]);} void push_down(int p) { maxv[ls(p)]=max(maxv[ls(p)],lazy[p]); maxv[rs(p)]=max(maxv[rs(p)],lazy[p]); lazy[ls(p)]=max(lazy[ls(p)],lazy[p]); lazy[rs(p)]=max(lazy[rs(p)],lazy[p]); lazy[p]=0; } void build(int rt,int l,int r) { lazy[rt]=0; if(l==r) {maxv[rt]=1; return ;} int mid = (l+r)>>1; build(lson);build(rson);push_up(rt); } int Querymax(int ql,int qr,int rt,int l,int r) { if(ql<=l && r<=qr) return maxv[rt]; if(lazy[rt]) push_down(rt); int Max=-INF ,mid=(l+r)>>1; if(ql<=mid) Max = max(Max,Querymax(ql,qr,lson)); if(qr>mid) Max = max(Max,Querymax(ql,qr,rson)); return Max; } void update(int ql,int qr,int v,int rt,int l,int r)//区间更新 { if(ql>r||qr<l) return; if(ql<=l && qr>=r) {lazy[rt]=max(lazy[rt],v),maxv[rt]=max(maxv[rt],v);return;} if(lazy[rt]) push_down(rt); int mid=(l+r)>>1; update(ql,qr,v,lson);update(ql,qr,v,rson); push_up(rt); } }tree; void dfs1(int u,int pre) { size[u]=1;dep[u]=dep[pre]+1;fa[u]=pre; int maxson=-1; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(v==pre) continue; dfs1(v,u); size[u]+=size[v]; if(size[v]>maxson) maxson=size[v],son[u]=v; } } void dfs2(int x,int topx)// { top[x]=topx;id[x]=++cnt;rk[cnt]=x; if(son[x]!=-1) dfs2(son[x],topx); for(int i=0;i<g[x].size();i++) { int v=g[x][i]; if(v==fa[x]||v==son[x]) continue; dfs2(v,v); } } int qmax(int x,int y) { int ans=1; for(;top[x]!=top[y];x=fa[top[x]]) { if(dep[top[x]]<dep[top[y]]) swap(x,y);// ans=max(tree.Querymax(id[top[x]],id[x],1,1,n),ans); } if(dep[x]>dep[y]) swap(x,y); ans=max(tree.Querymax(id[x],id[y],1,1,n),ans); return rk[ans]; } int main() { rd(n);int q;rd(q); FOR(i,1,n-1){int u,v;rd(u);rd(v);g[u].push_back(v);} mem(son,-1);dep[0]=0; dfs1(1,0);cnt=0;dfs2(1,1); tree.build(1,1,n); while(q--) { char s[11];scanf("%s",s);int x;rd(x); if(s[0]=='C') tree.update(id[x],id[x]+size[x]-1,id[x],1,1,n);//子树即为 else printf("%d\n",qmax(x,1));//询问路径最大值 } return 0; }