Top Cluster树分块-树剖模板

· · 个人记录

读了集训队论文周 欣 - 《浅谈一类树分块的构建算法及其应用》,然后按照自己理解实现了个巨大麻烦的写法,轻喷/kel。

支持树链加链求和子树加子树求和。

一些扩展暂时无限期咕咕咕,省选不退役再更新。

真正的树分块

const int N=2e5+5,M=1e6+5,INF=1e9+7,NB=1e5+5,B=317;
int n,m,r,a[N],val[N],head[N],idx;
struct node{int nex,to;}e[N<<1];
void add(int u,int v){e[++idx].nex=head[u];e[idx].to=v;head[u]=idx;return ;}
int top[N],dep[N],siz[N],son[N];
int fa[N];
int nnum,num[NB],Len[NB],bel[N];
int sum1[NB],sum2[NB],tg1[NB],tg2[NB];
int st[N],st_cnt;
//当前簇对应的点集,不包含上界点,加入时按照 DFS 序排序
int ner[N],up[N],dw[N];
//当前点最近的簇路径上点,当前点簇的上下界点
vector<int>G[N];
void ad(int x,int v){
    val[x]+=v;
    if(x!=up[x]&&x!=dw[x]){
        sum2[bel[x]]+=v;
        if(x==ner[x]) sum1[bel[x]]+=v;
    }
    return ;
}
void AddCluster(int u,int v){//建立树簇(u,v),且u是v的祖先
    nnum++;
    ner[u]=u;
    if(!v) v=st[st_cnt];//若没有下界点
    G[u].pb(v);//虚树
    for(int x=v;x!=u;x=fa[x]) ner[x]=x;
    for(int i=1;i<=st_cnt;i++){
        const int &x=st[i];
        up[x]=u,dw[x]=v;
        bel[x]=nnum;
        if(!ner[x]){
            int w=x;
            while(!ner[w]) w=fa[w];
            ner[x]=ner[w];
        }
        ad(x,a[x]);
    }
    num[nnum]=st_cnt-1;
    Len[nnum]=dep[v]-dep[u]-1;
    st_cnt=0;
    return ;
}
// s / s_cnt: 维护未归类点的栈
// los / vid: Dfs 之后未归类点的个数 / 应当作为下界点的编号
// inid: 进入点 u 时的 s_cnt
int s[N],s_cnt,inid[N],los[N],vid[N];

void Dfs(int x,int f){//Top Cluster分解
    dep[x]=dep[f]+1;siz[x]=1;
    fa[x]=f;inid[x]=s_cnt;los[x]=1;
    int v_num=0;
    for(int i=head[x];i;i=e[i].nex){
        int y=e[i].to;
        if(y==f) continue;
        s[++s_cnt]=y;
        Dfs(y,x);
        siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
        los[x]+=los[y];
        if(vid[y]) vid[x]=vid[y],v_num++;
    }
    if(los[x]>B||v_num>1||!f){
        vid[x]=x;los[x]=0;
        int nlos=0,nvid=0,pos=inid[x]+1;
        for(int i=head[x];i;i=e[i].nex){
            int y=e[i].to;
            if(y==f) continue;
            if(nlos+los[y]>B||(nvid&&vid[y])){
                while(pos<inid[y]&&pos<=s_cnt) st[++st_cnt]=s[pos],pos++;
                AddCluster(x,nvid);
                nvid=nlos=0;
            } 
            nlos+=los[y];
            if(vid[y]) nvid=vid[y];
        }
        while(pos<=s_cnt) st[++st_cnt]=s[pos++];
        AddCluster(x,nvid);
        s_cnt=inid[x];
    }
    return ;
}
void dfs1(int x,int f){//树剖求lca用
    if(x==son[f]) top[x]=top[f];
    else top[x]=x;
    if(son[x]) dfs1(son[x],x);
    for(int i=head[x];i;i=e[i].nex){
        int y=e[i].to;
        if(y==f||y==son[x]) continue;
        dfs1(y,x);
    }
    return ;
}
int LCA(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}

void ModifyRoad1(int x,int y,int v1){//修改(x,y)链(不包含y)
    int u=up[x],v=dw[y];
    if(bel[x]==bel[y]){//同一个簇内
        while(x!=y) ad(x,v1),x=fa[x];
        return ;
    }
    while(x!=u) ad(x,v1),x=fa[x];
    while(x!=v){
        const int &tmp=bel[x];
        ad(x,v1);
        sum1[tmp]+=Len[tmp]*v1,
        sum2[tmp]+=Len[tmp]*v1,
        tg1[tmp]+=v1,
        x=up[x];
    }
    while(x!=y) ad(x,v1),x=fa[x];
    return ;
}
void ModifyRoad(int x,int y,int v){//链加 (x,y)
    int lca=LCA(x,y);
    ModifyRoad1(x,lca,v);
    ModifyRoad1(y,lca,v);
    ad(lca,v);
    return ;
}
void Change(int x,int f,int v,int id){//更改子树当前簇内信息
    ad(x,v);
    for(int i=head[x];i;i=e[i].nex){
        int y=e[i].to;
        if(y==f||y==id) continue;
        Change(y,x,v,id);
    }
    return ;
}
void Change1(int x,int v){
    val[x]+=v;//界点修改
    for(int y:G[x]){
        const int &tmp=bel[y];
        sum1[tmp]+=v*Len[tmp];
        sum2[tmp]+=v*num[tmp];
        tg2[tmp]+=v;
        Change1(y,v);
    }
    return ;
}
void ModifyTree(int x,int v){
    if(x!=dw[x]&&x!=up[x]) Change(x,fa[x],v,dw[x]);
    int lca=LCA(x,dw[x]);
    if(lca!=x) return ;
    Change1(dw[x],v);
    return ;
}
int As(int x){
    int res=val[x];
    if(x!=up[x]&&x!=dw[x]){
        res+=tg2[bel[x]];
        if(x==ner[x]) res+=tg1[bel[x]];
    }
    return res;
}
int QueryRoad1(int x,int y){//查询(x,y)链(不包含y)
    int u=up[x],v=dw[y];
    int res=0;
    if(bel[x]==bel[y]){//同一个簇内
        while(x!=y) res+=As(x),x=fa[x];
        return res;
    }
    while(x!=u) res+=As(x),x=fa[x];
    while(x!=v){
        const int &tmp=bel[x];
        res+=As(x);
        res+=sum1[tmp];
        x=up[x];
    }
    while(x!=y) res+=As(x),x=fa[x];
    return res;
}
int QueryRoad(int x,int y){//修改路径
    int lca=LCA(x,y);
    int res=0;
    res=QueryRoad1(x,lca);
    res+=QueryRoad1(y,lca);
    return res+As(lca);
}
int Query1(int x,int f,int id){//查询除开界点的子树
    int res=As(x);
    for(int i=head[x];i;i=e[i].nex){
        int y=e[i].to;
        if(y==f||y==id) continue;
        res+=Query1(y,x,id);
    }
    return res;
}
int Query(int x){//查询界点子树
    int res=val[x];
    for(int y:G[x]){
        const int &tmp=bel[y];
        res+=sum2[tmp];
        res+=Query(y);
    }
    return res;
}
int QueryTree(int x){//子树查询
    int res=0;
    if(x!=dw[x]&&x!=up[x]) res+=Query1(x,fa[x],dw[x]);
    int lca=LCA(x,dw[x]);
    if(lca!=x) return res;
    res+=Query(dw[x]);
    return res;
}
inline void _Solve(){
    read(n,m,r,MOD);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        read(u,v);
        add(u,v),add(v,u);
    }
    Dfs(r,0);
    up[r]=dw[r]=ner[r]=r;
    val[r]=a[r];//需要特判根节点所在块
    dfs1(r,0);
    while(m--){
        int op,x,y,v;
        read(op);
        if(op==1){
            read(x,y,v);
            ModifyRoad(x,y,v);
        }
        else if(op==2){
            read(x,y);
            write(QueryRoad(x,y)),pc('\n');
        }
        else if(op==3){
            read(x,v);
            ModifyTree(x,v);
        }
        else{
            read(x);
            write(QueryTree(x)),pc('\n');
        }
    }
    return ;
}
/*

*/