【算法学习】树链剖分

普通dfs序

类二叉树先序

  • define dfn :dfs遍历的标号计数变量。

  • define id[x] x节点在遍历过程中被标记的dfn的值。

  • define idx[x]dfnx的节点是哪个节点。是id数组的逆

  • L[x] <==> id[x]:以x为根的子树中最小的 dfn

  • define R[x]:以x为根的子树中最大的 dfn

这样操作一个子树,就是映射到序列上一个连续的区间。把子树维护问题转化成数组维护问题
zngg的图:
image

树链剖分

树链剖分的本质是把树形结构压缩重构,使得在新的树上面暴力移动的复杂度不多于\(logN\)

树链剖分本身不算数据结构,只是把树用dfs重新整理。

dfs1预处理:对于子树轻重的划分

  1. 树上dp算出所有根的子树大小size,并记录深度deep
  2. 记录重儿子son
  • 重儿子:当前节点的所有儿子中size最大的
  • 轻儿子:当前节点的所有儿子中size非最大的
  • 定理:轻儿子的size必定小于等于当前子树size的一半
  • 定理扩展:树上每经过一条轻边,子树尺寸缩减至少一半(时间复杂度由来)
  • 链最多:\((N+1)/2\)

注意:

image

改俩点x和y就是改 index 中 “76” &“9825”

image

板子题: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;
}
posted @ 2021-10-08 16:50  qingyanng  阅读(37)  评论(1编辑  收藏  举报