線段樹的合併

原理和思路

思路

  • 我們常常會遇到一些問題對於每一個點都有它的一些信息
    然後我們需要查詢一段區間
    或者是樹上一個點的子樹信息

  • 此時我們便可以通過線段樹的合併來解決這些問題

原理

  • 我們通過把信息合併以後,利用合併後的線段樹來實現logn 的查詢
  • 有時候我們也可以事先預處理好以方便查詢

基本流程

  • 就像建主席樹一樣,對每一個點我們爲它造一個樹
  • 之後我們對不同的節點進行合併
  • 爲了使合併可持久化我們可以在合併的過程中新增節點
    代碼如下
int merge(int x,int y){
    if(!x||!y)return x|y;
    int t=++num;
    T[t]=T[x]+T[y];//收集兩者信息
    T[t].l=merge(T[x].l,T[y].l);
    T[t].r=merge(T[x].r,T[y].r);
    return t;
}
void insert(int &p,int x,int l,int r){
    p=++num;
    T[p].init();//初始化 
    if(l==r)return;
    int mid=l+r>>1;
    if(x<=mid)insert(T[p].l,x,l,mid);
    else insert(T[p].r,x,mid+1,r);
}
void Delt(int &p,int x,int pre,int l,int r,int k){
    if(!p||p==pre){
        p=++num;//爲了可初始化我們建一個新節點 
        T[p]=T[pre];
        T[p]-=k;
    }if(l==r)return;
    int mid=l+r>>1;
    if(x<=mid)Delt(T[p].l,x,T[pre].l,l,mid,k);
    else Delt(T[p].r,x,T[pre].r,mid+1,r,k);
}
int query(int p,int dl,int dr,int l,int r) {
    if(!p)return 0;
    if(dl==l&&dr==r)return T[p].v;
    int mid=l+r>>1;
    if(dr<=mid)return query(T[p].l,dl,dr,l,mid);
    if(mid<dl)return query(T[p].r,dl,dr,mid+1,r);
    return query(T[p].l,dl,mid,l,mid)+query(T[p].r,mid+1,dr,mid+1,r);
}

利用其他算法優化

  • 有時對於一些題目 對於每個節點有多個不同的信息我們可以建多個數或者用其他算法優化
  • 七彩樹(查詢每個子樹深度爲d有多少不同顏色)
    • 此題我們可以用dsu來優化
    • 具體流程:我們用dsu收集每個子樹中每個顏色的最小深度,在合併過程中刪去多餘點(深度較大的)
      代碼如下
void clear(int x) {
    for(int i=l[x]; i<=r[x]; i++) {
        int y=dfn[i];
        final_midep[col[y]]=0;
    }
}
void update(int x) {
    for(int i=l[x]; i<=r[x]; i++) {
        int y=dfn[i];
        if(!now_midep[col[y]])now_midep[col[y]]=dep[y];
        else now_midep[col[y]]=min(now_midep[col[y]],dep[y]);
    }
    for(int i=l[x]; i<=r[x]; i++) {
        int y=dfn[i];
        if(dep[y]!=now_midep[col[y]])continue;
        if(!final_midep[col[y]])final_midep[col[y]]=dep[y];

        else if(dep[y]<final_midep[col[y]]) {

            Delt(root[fa[x]],final_midep[col[y]],root[fa[x]]);//把深的節點刪除掉

            final_midep[col[y]]=dep[y];
        }
        else Delt(root[fa[x]],dep[y],root[fa[x]]);
        now_midep[col[y]]=0;
    }
}
void dsu(int x=1){
    insert(root[x],dep[x]);
    for(int i=h[x]; i; i=nx[i]) {
        int y=g[i];
        if(y!=son[x]) {
            dsu(y);
            root[x]=merge(root[x],root[y]);
            clear(y);//把和y有關的清空
        }
    }
    if(son[x]) {
        dsu(son[x]);
        root[x]=merge(root[x],root[son[x]]);
    }
    for(int i=h[x]; i; i=nx[i])if(g[i]!=son[x])update(g[i]); //更新節點
    if(final_midep[col[x]])Delt(root[x],final_midep[col[x]],root[x]);
    final_midep[col[x]]=dep[x];
}
發佈了95 篇原創文章 · 獲贊 183 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章