【Luogu P3384】樹鏈剖分模板

樹鏈剖分的基本思想是把一棵樹剖分紅若干條鏈,再利用線段樹等數據結構維護相關數據,能夠很是暴力優雅地解決不少問題。數據結構

樹鏈剖分中的幾個基本概念:
重兒子:對於當前節點的全部兒子中,子樹大小最大的一個兒子就是重兒子(子樹大小相同的則隨意取一個)
輕兒子:不是重兒子就是輕兒子
重邊:鏈接父節點和重兒子的邊
輕邊:鏈接父節點和輕兒子的邊
重鏈:相鄰重邊相連造成的鏈
值得注意的還有如下幾點:
葉子節點沒有重兒子也沒有輕兒子;
對於每一條重鏈,其起點必然是輕兒子;
單獨一個輕葉子節點也是一條重鏈;
結合上面三條能夠得出樹剖的一個性質:重鏈必然能夠囊括全部的節點。
樹鏈剖分
(圖片來源百度圖片,侵刪)
紅點標記的是輕兒子,粗線就是重鏈。結合圖片理解概念。函數

樹鏈剖分須要怎麼作呢?
一、用DFS給每個節點標記深度,父節點和重兒子。
二、用DFS按照DFS遍歷的順序給每個節點標記新的編號。關鍵點:先處理重兒子再處理輕兒子
解釋:先處理重兒子可讓重鏈上的每個點的編號連續。能夠觀察上圖,線上的數字就是DFS的順序。使編號連續後,咱們就可使用線段樹來維護數據了。
作完以上兩步就算是完成了樹鏈剖分了,接下來要作的就是利用其它數據結構來進行維護了。ui

void add(ll sta,ll to)
{
    edge[++cnt].to=to;
    edge[cnt].next=head[sta];
    head[sta]=cnt;
}//鏈式前向星存樹
void dfs1(ll now,ll fa,ll deep)
{   
    f[now]=fa;//記錄父節點
    d[now]=deep;//記錄深度(深度在區間求和時會用到)
    size[now]=1;//記錄子樹大小
    for (ll i=head[now];i!=0;i=edge[i].next)
    {
        if (edge[i].to==fa) continue;
        dfs1(edge[i].to,now,deep+1);
        size[now]+=size[edge[i].to];
        if (size[edge[i].to]>size[wson[now]]) wson[now]=edge[i].to;
        //取重兒子
    }
}
void dfs2(ll now,ll t)
{
    top[now]=t;//記錄節點所在重鏈的起點
    id[now]=++cnt;//按照順序編號
    rk[cnt]=now;//記錄第cnt個點表示的是now節點,建樹時會用到
    if (wson[now]) dfs2(wson[now],t);//優先處理重兒子
    for (ll i=head[now];i!=0;i=edge[i].next)
    {
        if (edge[i].to==wson[now]) continue;
        if (edge[i].to==f[now]) continue;
        dfs2(edge[i].to,edge[i].to);//一條重鏈的開頭必然是輕兒子,鏈頭即爲它自己
    }
}

樹上兩點的最短路徑修改操做:spa

void treeupd(ll x,ll y,ll num)
{
    while (top[x]!=top[y])
    {
        if (d[top[x]]>d[top[y]])
        {
            segupd(1,1,n,id[top[x]],id[x],num);
            //segupd爲線段樹的更新函數
            x=f[top[x]];
        }
        else 
        {
            segupd(1,1,n,id[top[y]],id[y],num);
            //segupd爲線段樹的更新函數
            y=f[top[y]];
        }
    }
    //這一個循環的目的是,只要這兩個節點不在一條重鏈上,
    //就讓比較深的那一個往上跳到另外一條鏈直到二者在同一條鏈上
    //又由於節點編號是連續的,因此能夠很方便地給整條鏈加上修改操做
    if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num);
    else segupd(1,1,n,id[y],id[x],num);
    //在最後二者位於同一條鏈上後,仍然要對他們兩個之間的節點進行修改。
}

求和操做再也不贅述,與上面的更新操做相似。code

完整代碼blog

#include<cstdio>
#include<algorithm>
#define lson root<<1
#define rson root<<1|1
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
struct data
{
    ll to,next;
}edge[200005];
ll cnt,head[200005],f[100005],d[100005],size[100005],wson[100005],top[100005],id[100005];
ll rk[100005],tree[800005],n,m,a[100005],p,tag[800005],r,x,y,z,flag;
void add(ll sta,ll to)
{
    edge[++cnt].to=to;
    edge[cnt].next=head[sta];
    head[sta]=cnt;
}
void dfs1(ll now,ll fa,ll deep)
{   
    f[now]=fa;
    d[now]=deep;
    size[now]=1;
    for (ll i=head[now];i!=0;i=edge[i].next)
    {
        if (edge[i].to==fa) continue;
        dfs1(edge[i].to,now,deep+1);
        size[now]+=size[edge[i].to];
        if (size[edge[i].to]>size[wson[now]]) wson[now]=edge[i].to;
    }
}
void dfs2(ll now,ll t)
{
    top[now]=t;
    id[now]=++cnt;
    rk[cnt]=now;
    if (wson[now]) dfs2(wson[now],t);
    for (ll i=head[now];i!=0;i=edge[i].next)
    {
        if (edge[i].to==wson[now]) continue;
        if (edge[i].to==f[now]) continue;
        dfs2(edge[i].to,edge[i].to);
    }
}
void build(ll root,ll l,ll r)
{
    if (l==r) 
    {
        tree[root]=a[rk[l]]%p;
        return ;
    }
    build(lson,l,mid);
    build(rson,mid+1,r);
    tree[root]=(tree[lson]+tree[rson])%p;   
}
void push_down(ll root,ll l,ll r)
{
    if (tag[root]==0) return ;
    tag[lson]+=tag[root];
    tag[rson]+=tag[root];
    tree[lson]+=tag[root]*(mid-l+1);
    tree[rson]+=tag[root]*(r-mid);
    tag[lson]%=p;
    tag[rson]%=p;
    tree[lson]%=p;
    tree[rson]%=p;
    tag[root]=0;
}
void segupd(ll root,ll l,ll r,ll al,ll ar,ll num)
{
    if (ar<l||r<al) return ;
    if (al<=l&&r<=ar)
    {
        tree[root]+=num*(r-l+1);
        tag[root]+=num;
        tree[root]%=p;
        tag[root]%=p;
        return ;
    }
    push_down(root,l,r);
    segupd(lson,l,mid,al,ar,num);
    segupd(rson,mid+1,r,al,ar,num);
    tree[root]=(tree[lson]+tree[rson])%p;
}
ll query(ll root,ll l,ll r,ll al,ll ar)
{
    if (ar<l||r<al) return 0;
    if (al<=l&&r<=ar) return tree[root]%p;
    push_down(root,l,r);
    return (query(lson,l,mid,al,ar)+query(rson,mid+1,r,al,ar))%p;
}
ll getsum(ll x,ll y)
{
    ll sum=0;
    while (top[x]!=top[y])
    {
        if (d[top[x]]>d[top[y]])
        {
            sum=(sum+query(1,1,n,id[top[x]],id[x]))%p;
            x=f[top[x]];
        }
        else 
        {
            sum=(sum+query(1,1,n,id[top[y]],id[y]))%p;
            y=f[top[y]];
        }
    }
    if (id[x]<=id[y]) sum=(sum+query(1,1,n,id[x],id[y]))%p;
    else sum=(sum+query(1,1,n,id[y],id[x]))%p;
    return sum;
}
void treeupd(ll x,ll y,ll num)
{
    while (top[x]!=top[y])
    {
        if (d[top[x]]>d[top[y]])
        {
            segupd(1,1,n,id[top[x]],id[x],num);
            x=f[top[x]];
        }
        else 
        {
            segupd(1,1,n,id[top[y]],id[y],num);
            y=f[top[y]];
        }
    }
    if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num);
    else segupd(1,1,n,id[y],id[x],num);
}
int main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
    for (ll i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for (ll i=1;i<n;i++)
    {
        scanf("%lld%lld",&x,&y);
        add(x,y);
        add(y,x);
    }
    cnt=0;
    dfs1(r,0,0);
    dfs2(r,r);
    build(1,1,n);
    for (ll i=1;i<=m;i++)
    {
        scanf("%lld",&flag);
        if (flag==1)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            treeupd(x,y,z);
        }
        if (flag==2)
        {
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",getsum(x,y));
        }
        if (flag==3)
        {
            scanf("%lld%lld",&x,&z);
            segupd(1,1,n,id[x],id[x]+size[x]-1,z);
            //這裏能夠結合圖片理解一下爲何。 
        }
        if (flag==4)
        {
            scanf("%lld",&x);
            printf("%lld\n",query(1,1,n,id[x],id[x]+size[x]-1));
        }
    }
    return 0;
}
相關文章
相關標籤/搜索