20230630树剖学习笔记

发布时间 2023-06-30 15:12:15作者: Zimo_666

树链剖分

重链剖分

定义 重子节点 表示其子节点中子树最大的子结点。如果有多个子树最大的子结点,取其一。如果没有子节点,就无重子节点。

定义 轻子节点 表示剩余的所有子结点。

从这个结点到重子节点的边为 重边

到其他轻子节点的边为 轻边

若干条首尾衔接的重边构成 重链

把落单的结点也当作重链,那么整棵树就被剖分成若干条重链。

接着我们用\(rnk\)表示\(DFS\)序对应的的节点编号,有\(rnk(dfn(x))=x\)

我们先用\(dfs1\)维护一些基本信息和重儿子。

void dfs(int x,int fat){
    dep[x]=dep[fat]+1;
    fa[x]=fat;siz[x]=1;
    for(int k:G[x]){
        if(k!=fat){
            dfs(k,x);
            siz[x]+=siz[k];
            if(siz[k]>siz[bgs[x]]) bgs[x]=k;
        }
    }
}
void DFS(int x,int fat,int tp){
    top[x]=tp;
    if(bgs[x]){
        DFS(bgs[x],x,tp);
    }
    for(int k:G[x]){
        if(k!=fat&&k!=bgs[x]) DFS(k,x,k);
    }
}

我们考虑\(dfs2\)的实现方法,显然我们先表示节点\(top\)表示为 所在 重链 的顶部节点(深度最小)。

而后我们先把重儿子所在路径上的所有\(top\)记为该点,不是重儿子的链我们考虑\(top=\)本身。

树上每个节点都属于且仅属于一条重链

所有的重链将整棵树 完全剖分

求LCA

不断向上跳重链,当跳到同一条重链上时,深度较小的结点即为 LCA。

向上跳重链时需要先跳所在重链顶端深度较大的那个。

int lca(int x,int y){
    while(top[x]^top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}

树的统计(树剖)

题意

求树上两点路径和,路径上的最大值,修改单点点权。

分析

就拿计算路径上和来举例子,首先我们有一个线段树上的\(query\_max\)函数,而后我们对于两个点\(x,y\)每次我们当他们所在链的\(top\)不相等时找到\(dep[top[x]]\)大的那个然后令\(ans+=\)这条路径上的\(sum\)而后我们让\(x\)跳到原来链的\(top\)\(fa\)上,跳到另外一条链。直到\(top[x]=top[y]\)。而后我们再加上深度较低的那个点和较高那个点的路径上的和。又因为赋值\(dfn\)时我们在一条链上总有若\(dep[x]>dep[y]\)\(dfn[x]>dfn[y]\)所以我们注意提交线段树处理时\(dfn[x]<dfn[y]\)\(dep[x]<dep[y]\)。这样我们就可以得到最终的\(Ans\)了。

路径最大值同理。

树上单点修改直接线段树暴力修改即可注意在\(dfs\)时我们记录\(rnk[i]\)表示\(dfn\)\(i\)的点编号是多少。这样我们可以方便的使用线段树进行修改。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 5;

#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)

std::vector<int> G[MAXN << 1];

int top[MAXN << 1],cnt,dfn[MAXN<<1];
int fa[MAXN << 1], bgs[MAXN << 1], dep[MAXN << 1], siz[MAXN << 1] , rnk[MAXN<<1];
int w[MAXN];
struct tree{
    void dfs(int x,int fat){
        dep[x]=dep[fat]+1;
        fa[x]=fat;siz[x]=1;
        for(int k:G[x]){
            if(k!=fat){
                dfs(k,x);
                siz[x]+=siz[k];
                if(siz[k]>siz[bgs[x]]) bgs[x]=k;
            }
        }
    }
    void DFS(int x,int fat,int tp){
        dfn[x]=++cnt;
        top[x]=tp;
        rnk[cnt]=x;
        if(bgs[x]){
            DFS(bgs[x],x,tp);
        }
        for(int k:G[x]){
            if(k!=fat&&k!=bgs[x]) DFS(k,x,k);
        }
    }
}_sp;
int n;
struct seg_tree{
    int maxx[MAXN],sum[MAXN];
    void pushup(int k,int l,int r){sum[k]=sum[ls]+sum[rs];maxx[k]=max(maxx[ls],maxx[rs]);}
    void build(int k,int l,int r){
        if(l==r) return sum[k]=maxx[k]=w[rnk[l]],void();
        build(ls,l,mid),build(rs,mid+1,r);
        pushup(k,l,r);
    }
    void modify(int k,int l,int r,int x,int val){
        if(l==r) return sum[k]=maxx[k]=val,void();
        if(x<=mid) modify(ls,l,mid,x,val);
        else modify(rs,mid+1,r,x,val);
        pushup(k,l,r);
    }
    int query_max(int k,int l,int r,int x,int y){
        if(x<=l&&y>=r) return maxx[k];
        int res=-0x3f3f3f3f;
        if(x<=mid) res=max(res,query_max(ls,l,mid,x,y));
        if(y>=mid+1) res=max(res,query_max(rs,mid+1,r,x,y));
        return res;
    }
    int query_sum(int k,int l,int r,int x,int y){
        if(x<=l&&y>=r) return sum[k];
        int res=0;
        if(x<=mid) res+=query_sum(ls,l,mid,x,y);
        if(y>=mid+1) res+=query_sum(rs,mid+1,r,x,y);
        return res;
    }
}T;
int tree_query_max(int x,int y){
  int ans = -40000;
  while (top[x] != top[y])
  {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    ans = max(ans, T.query_max(1, 1, n, dfn[top[x]], dfn[x]));
    x = fa[top[x]];
  }
  if (dep[x] > dep[y]) swap(x, y);
  ans = max(ans, T.query_max(1, 1, n, dfn[x], dfn[y]));
  return ans;
}
int tree_query_sum(int x,int y){
  int ans = 0;
  while (top[x] != top[y])
  {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    ans+=T.query_sum(1, 1, n, dfn[top[x]], dfn[x]);
    x = fa[top[x]];
  }
  if (dep[x] > dep[y]) swap(x, y);
  ans+=T.query_sum(1, 1, n, dfn[x], dfn[y]);
  return ans;
}
int main(){
    scanf("%d", &n);
    for (int i = 1, u, v; i < n; ++i)
    {
        scanf("%d%d", &u, &v);
        G[u].push_back(v), G[v].push_back(u);
    }
    for (int i = 1; i <= n; ++i) scanf("%d", w + i);
    _sp.dfs(1, 0);
    _sp.DFS(1,0,1);
    T.build(1, 1, n);
    int q;
    scanf("%d", &q);
    char s[10]; int x, y;
    while (q--)
    {
        scanf("%s%d%d", s, &x, &y);
        if (s[0] == 'C') T.modify(1, 1, n, dfn[x], y);
        else if (s[1] == 'M')
        {
          printf("%d\n",tree_query_max(x,y));
        }
        else
        {
          printf("%d\n",tree_query_sum(x,y));
        }
    }
  return 0;
}