關於樹鏈剖分的一些算法

參考和學習自:dalao's bloghtml

樹鏈剖分,一個神奇的算法,用來解決樹上的求和,找最值,修改以及許多玄學操做的算法算法

其實,上面講的求和,找最值和修改都是像線段樹同樣對於序列上的操做。固然若是你用主席樹,Splay等高級數據結構該能夠作更多的事情數組

而樹剖就是這樣,它能使得那些維護序列的操做和數據結構能夠被用在樹上數據結構

其實也是一個預處理同樣的東西了函數

首先先明確一些概念:(不懂也沒事)學習

  • 重結點:子樹結點數目最多的結點;ui

  • 輕節點:父親節點中除了重結點之外的結點;spa

  • 重邊:父親結點和重結點連成的邊;code

  • 輕邊:父親節點和輕節點連成的邊;htm

  • 重鏈:由多條重邊鏈接而成的路徑;

  • 輕鏈:由多條輕邊鏈接而成的路徑;

而後咱們就能夠經過一種方法把樹上的節點從新編號,使得能夠向維護序列同樣的維護它們

咱們肯定一種標號方式,優先對重鏈上的點進行編號,而後再輕鏈

這樣,咱們發現:一棵子樹中,全部的點都被連續編號了,且重鏈上的點的標號較小

所以咱們在查詢時若兩個點在同一條重鏈上就能夠直接查詢(編號是連續的)

若是不是的話就把一個點(深度較深)先移到當前這條重鏈的頂端,而後向上經過一條輕邊便可

具體看代碼吧,畢竟有些東西只可意會,不可言傳

這裏給出一些數組的意義

  • dep[]全部節點的深度

  • father[]全部節點的直系father

  • size[]全部以當前節點爲根的子樹節點個數(包括本身)

  • id[]每一個節點樹剖後的編號

  • s[]每一個節點樹剖後新的值,與id相對應

  • son[]每一個節點的重兒子

  • top[]每一個節點所在的重鏈的鏈的頂端

線段樹就不介紹了吧

CODE

#include<cstdio>
#include<cstring>
const int N=100005;
typedef long long LL;
using namespace std;
struct segtree
{
    LL add,sum;
}tree[N<<2];
struct edge
{
    int to,next;
}e[N<<1];
int n,q,root,p,a[N],head[N],cnt,dep[N],father[N],size[N],id[N],s[N],son[N],top[N],opt,x,y,z,tot;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void write(LL x)
{
    if (x/10) write(x/10);
    putchar(x%10+'0');
}
inline void add(int x,int y)
{
    e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
}
inline void swap(int &a,int &b)
{
    int t=a; a=b; b=t;
}
inline void up(int root)
{
    tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p;
}
inline void down(int root,int l,int r)
{
    if (tree[root].add)
    {
        tree[root<<1].add=(tree[root<<1].add+tree[root].add)%p;
        tree[root<<1|1].add=(tree[root<<1|1].add+tree[root].add)%p;
        tree[root<<1].sum=(tree[root<<1].sum+tree[root].add*l)%p;
        tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].add*r)%p;
        tree[root].add=0;
    }
}
inline void build(int root,int l,int r)
{
    if (l==r)
    {
        tree[root].sum=s[l];
        return;
    }
    int mid=l+r>>1;
    build(root<<1,l,mid); build(root<<1|1,mid+1,r);
    up(root);
}
inline void modify(int root,int l,int r,int beg,int end,int k)
{
    if (l>=beg&&r<=end)
    {
        tree[root].add=(tree[root].add+k)%p;
        tree[root].sum=(tree[root].sum+k*(r-l+1))%p;
        return;
    }
    int mid=l+r>>1;
    down(root,mid-l+1,r-mid);
    if (beg<=mid) modify(root<<1,l,mid,beg,end,k);
    if (end>mid) modify(root<<1|1,mid+1,r,beg,end,k);
    up(root);
}
inline LL query(int root,int l,int r,int beg,int end)
{
    if (l>=beg&&r<=end) return tree[root].sum;
    int mid=l+r>>1;
    LL res=0;
    down(root,mid-l+1,r-mid);
    if (beg<=mid) res=(res+query(root<<1,l,mid,beg,end))%p;
    if (end>mid) res=(res+query(root<<1|1,mid+1,r,beg,end))%p;
    return res;
}
inline void DFS1(int now,int fa,int d)
{
    father[now]=fa; dep[now]=d;
    size[now]=1; int res=-1;
    for (register int i=head[now];i!=-1;i=e[i].next)
    if (e[i].to!=fa)
    {
        DFS1(e[i].to,now,d+1);
        size[now]+=size[e[i].to];
        if (size[e[i].to]>res) res=size[e[i].to],son[now]=e[i].to;
    }
}
inline void DFS2(int now,int topf)
{
    id[now]=++tot; s[tot]=a[now];
    top[now]=topf;
    if (!son[now]) return;
    DFS2(son[now],topf);
    for (register int i=head[now];i!=-1;i=e[i].next)
    if (e[i].to!=father[now]&&e[i].to!=son[now]) DFS2(e[i].to,e[i].to);
}
inline void change(int x,int y,int z)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        modify(1,1,n,id[top[x]],id[x],z);
        x=father[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    modify(1,1,n,id[y],id[x],z);
}
inline void updata(int x,int z)
{
    modify(1,1,n,id[x],id[x]+size[x]-1,z);
}
inline LL get_sec(int x,int y)
{
    LL res=0;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        res=(res+query(1,1,n,id[top[x]],id[x]))%p;
        x=father[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    res=(res+query(1,1,n,id[y],id[x]))%p;
    return res;
}
inline LL get_son(int x)
{
    return query(1,1,n,id[x],id[x]+size[x]-1);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i;
    memset(head,-1,sizeof(head));
    memset(e,-1,sizeof(e));
    read(n); read(q); read(root); read(p);
    for (i=1;i<=n;++i)
    read(a[i]);
    for (i=1;i<n;++i)
    {
        read(x); read(y);
        add(x,y); add(y,x);
    }
    DFS1(root,-1,0);
    DFS2(root,root);
    build(1,1,n);
    while (q--)
    {
        read(opt); read(x);
        if (opt==1) read(y),read(z),change(x,y,z%p);
        if (opt==2) read(y),write(get_sec(x,y)),putchar('\n');
        if (opt==3) read(z),updata(x,z%p);
        if (opt==4) write(get_son(x)),putchar('\n');
    }
    return 0;
}

樹剖CODE仍是比較長的

建議你們碼的順序:主程序+其餘輔助函數+樹剖預處理函數+線段樹函數

一鼓作氣最重要

還有另一道板子題Luogu P2590改一下線段樹便可

相關文章
相關標籤/搜索