【算法学习】树链剖分
普通dfs序
类二叉树先序
-
define dfn :dfs遍历的标号计数变量。
-
define id[x] :x节点在遍历过程中被标记的dfn的值。
-
define idx[x]:dfn为x的节点是哪个节点。是id数组的逆
-
L[x] <==> id[x]:以x为根的子树中最小的 dfn
-
define R[x]:以x为根的子树中最大的 dfn
这样操作一个子树,就是映射到序列上一个连续的区间。把子树维护问题转化成数组维护问题
zngg的图:
树链剖分
树链剖分的本质是把树形结构压缩重构,使得在新的树上面暴力移动的复杂度不多于\(logN\)
树链剖分本身不算数据结构,只是把树用dfs重新整理。
dfs1预处理:对于子树轻重的划分
- 树上dp算出所有根的子树大小size,并记录深度deep
- 记录重儿子son
- 重儿子:当前节点的所有儿子中size最大的
- 轻儿子:当前节点的所有儿子中size非最大的
- 定理:轻儿子的size必定小于等于当前子树size的一半
- 定理扩展:树上每经过一条轻边,子树尺寸缩减至少一半(时间复杂度由来)
- 链最多:\((N+1)/2\)条
注意:
改俩点x和y就是改 index 中 “76” &“9825”
板子题:P2146 [NOI2015] 软件包管理器
The topic conditions:
软件包 a 依赖软件包 b,那么安装软件包 a以前,必须先安装软件包 b。同时,如果想要卸载软件包 b,则必须卸载软件包 a。
Query:
安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包。
Subject analysis:
安装包之间是树形关系
- 安装一个包x,根1到x都得安装 树链剖分处理
- 在剖分后的树上找1和x的LCA,1-x路径上的点就是必须安装的点。
- 卸载一个包下,以x为根的子树都得卸载 普通dfs序处理
- 处理后,树的编号x映射成id[x],处理x子树就是区间id[x]-R[x]
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <stack>
#include <cstdio>
#include <queue>
#include <set>
#include<sstream>
#include <cstring>
#include <cmath>
#include <bitset>
#include <ctime>
//#pragma GCC optimize(2);
#define IOS ios::sync_with_stdio(false);
#define mm(a, b) memset(a, b, sizeof(a))
const double PI = acos(-1.0);
typedef long long ll;
const int N = 1e5+5;
const double eps =1e-8;
const ll mod = 1e9+7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double maxd = -1e9;
const int maxn = 500;
using namespace std;
typedef pair<string,int> PII;
int h[N*2],e[N*2],ne[N*2],idx;
int top[N],deep[N],fa[N],son[N],sz[N],R[N];
int a[N],n;
int id[N],rev[N],dfn;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//预处理size,分轻重
void dfs1(int u,int dep,int f){
sz[u]=1;
deep[u]=dep;
fa[u]=f;
son[u]=0;
int v;
for(int i=h[u];~i;i=ne[i]){
v = e[i];
if(v==f)continue;
dfs1(v,dep+1,u);
sz[u] += sz[v];
//son记录重儿子
if(sz[v]>sz[son[u]])son[u]=v;
}
}
//链分解,跑普通dfs序
void dfs2(int u,int tp){
id[u] = ++dfn;
rev[dfn] = u;
top[u] = tp;//记录链头
if(son[u]) dfs2(son[u],tp);//处理重链
else{
R[u] = dfn; return ;
}
int v;
for(int i=h[u];~i;i=ne[i]){
v =e[i];
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
R[u]=dfn;
}
struct node//位运算和其他符号隔个空格
{
ll val,lazy;
}segtree[N<<2];
void push_up(int rt)
{
segtree[rt].val=segtree[rt<<1].val+segtree[rt<<1|1].val;
}
void push_down(int l,int r,int rt)
{
if(segtree[rt].lazy==-1)return ;
int mid=l+r>>1;
segtree[rt<<1].val=segtree[rt].lazy*(mid-l+1);
segtree[rt<<1|1].val=segtree[rt].lazy*(r-mid);
segtree[rt<<1].lazy=segtree[rt].lazy;
segtree[rt<<1|1].lazy=segtree[rt].lazy;
segtree[rt].lazy=-1;
}
void modify(int a,int b,int c,int l,int r,int rt)
{
if(l>b||r<a)return ;
if(l>=a&&r<=b)
{
segtree[rt].val=c*(r-l+1);
segtree[rt].lazy=c;
return ;
}
push_down(l,r,rt);
int mid=l+r>>1;
modify(a,b,c,l,mid,rt<<1);
modify(a,b,c,mid+1,r,rt<<1|1);
push_up(rt);
}
ll query(int a,int b,int l ,int r,int rt)
{
if(l>b||r<a)return 0;
if(l>=a&&r<=b)
return segtree[rt].val;
push_down(l,r,rt);
int mid=l+r>>1;
return query(a,b,l,mid,rt<<1 )+query(a,b,mid+1,r,rt<<1|1);
}
void build(int l,int r,int rt)
{
segtree[rt].lazy=-1;
if(l==r)
{
segtree[rt].val=0;
return ;
}
int mid=l+r>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
int chain_add(int x,int y,int val){ //修改查询合一起
int ans=0;
//找x和y的lca
while(top[x]^top[y]){
//深度大的网上调,直接到下一缩点(链)
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=query(id[top[x]],id[x],1,n,1);//查询跳过的这段
modify(id[top[x]],id[x],val,1,n,1);//修改跳过的这段
x=fa[top[x]];
}
//while之后,跑到了一个缩点(链),谁deep小谁是lca
if(deep[x]>deep[y]) swap(x,y);
ans+=query(id[x],id[y],1,n,1);//查询跳过的这段
modify(id[x],id[y],val,1,n,1);//修改跳过的这段
return ans;
}
//int chain_query(int x,int y){
// int ans=0;
// while(top[x]^top[y]){
// if(deep[top[x]]<deep[top[y]])
// swap(x,y);
// ans+=query(id[top[x]],id[x],1,n,1);
// x=fa[top[x]];
// }
// if(deep[x]>deep[y])
// swap(x,y);
// ans+=query(id[x],id[y],1,n,1);
// return ans;
//}
int main(){
int x;
cin>>n;
memset(h,-1,sizeof h);
for(int i=2;i<=n;i++){
cin>>x;++x;
add(x,i);
add(i,x);
}
dfs1(1,1,0);
dfs2(1,1);
build(1,n,1);
string opt;int q;
cin>>q;
while(q--){
cin>>opt>>x; ++x;
if(opt[0]=='i'){
cout<<deep[x]-chain_add(x,1,1)<<endl;//需要的减去已经有的
}else{
cout<<query(id[x],R[x],1,n,1)<<endl;
modify(id[x],R[x],0,1,n,1);
}
}
return 0;
}