【模板】樹鏈剖分

慘了慘了我好喜歡這個算法嗚嗚嗚快超過\(MST\)

樹剖涉及的知識:\(LCA\)(最近公共祖先/倍增),線段樹


已知一棵包含N個結點的樹(連通且無環),每個節點上包含一個數值,需要支持以下操作:

操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z

操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和

操作3: 格式: 3 x z 表示將以x爲根節點的子樹內所有節點值都加上z

操作4: 格式: 4 x 表示求以x爲根節點的子樹內所有節點值之和

(我理解中的)樹剖就是將樹上的問題,轉化爲線性來處理。

通過兩次搜索,對每個結點加以一些標記,其中還包括對結點進行新的編號

編號之後就滿足一個結點爲根的子樹,它們的新編號是連續的。

於是就可以維護區間和了w!(線段樹好啊233)

(懶得寫太多了)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN 233333
//變量們!
int n,m,r,mod;

int tot=0,cnt=0;

int ans[MAXN<<2],tag[MAXN<<2];

struct qwq
{
    int nex,to;
}e[MAXN];
int h[MAXN];

int w1[MAXN],w2[MAXN];

int dep[MAXN],top[MAXN],siz[MAXN],fa[MAXN],id[MAXN],son[MAXN];
//end. 

void add(int x,int y)
{
    e[++tot].to=y;
    e[tot].nex=h[x];
    h[x]=tot;
}


//seg_tree
#define leftson cur<<1
#define rightson cur<<1|1
#define mid ((l+r)>>1)
#define push_up ans[cur]=ans[leftson]+ans[rightson]; ans[cur]%=mod
#define push_down lazyadd(leftson,l,mid,tag[cur]); lazyadd(rightson,mid+1,r,tag[cur]); tag[cur]=0
inline void lazyadd(int cur,int l,int r,int del)
{
    tag[cur]+=del;
    ans[cur]+=del*(r-l+1);
    ans[cur]%=mod;
}
inline void build(int cur,int l,int r)
{
    if (l==r)
    {
        ans[cur]=w2[l]%mod;
        return;
    }
    build(leftson,l,mid);
    build(rightson,mid+1,r);
    push_up;
}
inline void change(int adl,int adr,int cur,int l,int r,int del)
{
    if(adl<=l&&r<=adr)
    {
        ans[cur]+=del*(r-l+1)%mod;
        tag[cur]+=del;
        return;
    }
    push_down;
    if (adl<=mid) change(adl,adr,leftson,l,mid,del);
    if (adr>mid) change(adl,adr,rightson,mid+1,r,del);
    push_up;
}

#define ll long long

ll query(int ql,int qr,int cur,int l,int r)
{
    if (ql<=l&&r<=qr)
    {
        return ans[cur];
    }
    push_down;
    ll answer=0;
    if (ql<=mid) answer+=query(ql,qr,leftson,l,mid)%mod;
    if (qr>mid) answer+=query(ql,qr,rightson,mid+1,r)%mod;
    return answer%mod;
}
//end.
inline void dfs_fir(int x,int f,int dept)
{
    dep[x]=dept;
    fa[x]=f;
    siz[x]=1;
    int maxn=-1;
    for (int i=h[x],y;i;i=e[i].nex)
    {
        y=e[i].to;
        if (y==f) continue;
        dfs_fir(y,x,dept+1);
        siz[x]+=siz[y];
        if (siz[y]>maxn)
        {
            son[x]=y;
            maxn=siz[y];
        }
    }
}

inline void dfs_sec(int x,int ft)
{
    id[x]=++cnt;
//  printf("id::%d",cnt);
    w2[cnt]=w1[x];
    top[x]=ft;
    if (!son[x]) return;
    dfs_sec(son[x],ft);
    int y;
    for (int i=h[x];i;i=e[i].nex)
    {
        y=e[i].to;
        if (y==fa[x]||y==son[x]) continue;
        dfs_sec(y,y);
    }
}
inline ll query_ans(int x,int y)
{
    ll answ=0;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        answ+=query(id[top[x]],id[x],1,1,n)%mod;
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    answ+=query(id[x],id[y],1,1,n);
    
    return answ%mod;
}

inline void upd_tree(int x,int y,int del)
{
    del%=mod;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        change(id[top[x]],id[x],1,1,n,del);
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    change(id[x],id[y],1,1,n,del);
}

inline ll query_sontree(int x)
{
    return query(id[x],id[x]+siz[x]-1,1,1,n)%mod;
}

inline void upd_sontree(int x,int del)
{
    change(id[x],id[x]+siz[x]-1,1,1,n,del);
}


//////
int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&mod);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&w1[i]);
    }
    for (int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs_fir(r,0,1);
    dfs_sec(r,r);
    build(1,1,n);
    int q,x,y,z;
    while (m--)
    {
        scanf("%d",&q);
        if (q==1)
        {
            scanf("%d%d%d",&x,&y,&z);
            upd_tree(x,y,z);
            continue;
        }
        if (q==2)
        {
            scanf("%d%d",&x,&y);
            printf("%lld\n",query_ans(x,y));
            continue;
        }
        if (q==3)
        {
            scanf("%d%d",&x,&y);
            upd_sontree(x,y);
            continue;
        }
        scanf("%d",&x);
        printf("%lld\n",query_sontree(x));
    }
    return 0;
}

(因爲是模板所以把涉及到的知識點的標籤都加上了w)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章