树链剖分

树链剖分

没有好好改题,来学数据结构(我没脸)。

基本概念:

  重儿子:子树结点数目最多的结点。

  轻儿子:父亲节点中除了重儿子以外的结点。

  重边:父亲结点和重结点连成的边。

  轻边:父亲节点和轻节点连成的边。

  重链:由多条重边连接而成的路径。

  轻链:由多条轻边连接而成的路径。

数组定义:

  $siz[u]$:以$u$为根的子树节点个数

  $top[u]$:当前节点所在重链的顶端节点

  $son[u]$:$u$的重儿子

  $dep[u]$:结点$u$的深度

  $fa[u]\ $:结点$u$的父亲节点

  $tid[u]$:树中每个节点剖分以后的新编号($DFS$的执行顺序)

  $rnk[u]$:$DFS$序中节点在树中的位置

预处理:

  两遍$dfs$。

  第一遍$dfs$,处理出每个点的$dep[u],fa[u],son[u],siz[u]$。

  第二遍$dfs$,处理出每个点的$top[u],tid[u],rnk[u]$,即将各个重结点连接成重链,轻节点连接成轻链,并且将重链(其实就是一段区间)用数据结构(一般是树状数组或线段树)来进行维护,并且为每个节点进行编号。

应用:

  1.单点修改:按新编号在线段树上修改。

  2.路径修改(查询):

    (1).修改点x到点y路径上各点的值

      需要一个神奇的跳链操作。

      具体操作为:

        $u$和$v$在同一条重链上时,直接线段树区间修改即可。

        $u$和$v$不在同一条重链上时,我们要想办法把它们往同一条重链上靠。

          $1$.$fa[top[u]]$与$v$在同一条重链上,做两次区间修改即可。

          $2$.$u$经过若干条重链与轻边后和$v$在同一条重链上,多次做区间修改即可。

          $3$.$u$和$v$都经过若干条重链与轻边后在同一条重链上,那么每次找到深度较深的点$x$,做一次区间修改后,再将该点跳到$fa[top[x]]$,直到$u$和$v$在同一条重链上

    (2).查询点x到点y路径上各点的值

      同上。

    (3).修改点x子树上各点的值

      考虑到子树内dfs序是相连的。

      所以被修改区间是一个连续的区间,所以直接在线段树上修改区间$[tid[i],tid[i]+siz[i]-1]$即可。

    (4).查询点x子树上各点的值

      同上。

模板:

  颓的代码。。。

 

#include <bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
inline ll read()
{
    register ll x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[36];int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct node{
    int to,next;
}e[N<<1];
int head[N],cnt=0;
inline void add(register int u,register int v)
{
    e[++cnt]=(node){v,head[u]};
    head[u]=cnt;
}
ll ch[N];
ll n,m,rt,mod;
ll size[N],dep[N],fa[N],son[N];
ll tot=0,dl[N],a[N],top[N];
inline void dfs1(register int x)
{
    size[x]=1;
    for(register int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa[x])
        {
            dep[e[i].to]=dep[x]+1;
            fa[e[i].to]=x;
            dfs1(e[i].to);
            size[x]+=size[e[i].to];
            if(size[e[i].to]>size[son[x]])
                son[x]=e[i].to;
        }
}
inline void dfs2(register int x,register int t)
{
    dl[x]=++tot;
    a[tot]=ch[x];
    top[x]=t;
    if(son[x])
        dfs2(son[x],t);
    for(register int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa[x]&&e[i].to!=son[x])
            dfs2(e[i].to,e[i].to);
}
ll sum[N<<3],tag[N<<3];
inline void pushup(register int x)
{
    sum[x]=sum[x<<1]+sum[x<<1|1];
    sum[x]%=mod;
}
inline void build(register int x,register int l,register int r)
{
    if(l==r)
    {
        sum[x]=a[l];
        tag[x]=0;
        return;
    }
    int mid=l+r>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
inline void pushdown(register int x,register int l,register int r)
{
    int ls=x<<1,rs=x<<1|1,mid=l+r>>1;
    sum[ls]+=(mid-l+1)*tag[x];
    sum[rs]+=(r-mid)*tag[x];
    tag[ls]+=tag[x];
    tag[rs]+=tag[x];
    sum[ls]%=mod;
    sum[rs]%=mod;
    tag[ls]%=mod;
    tag[rs]%=mod;
    tag[x]=0;
}
inline void update(register int x,register int l,register int r,register int L,register int R,register int k)
{
    if(L<=l&&r<=R)
    {
        sum[x]+=(r-l+1)*k;
        tag[x]+=k;
        sum[x]%=mod;
        tag[x]%=mod;
        return;
    }
    if(tag[x])
        pushdown(x,l,r);
    int mid=l+r>>1;
    if(L<=mid)
        update(x<<1,l,mid,L,R,k);
    if(R>=mid+1)
        update(x<<1|1,mid+1,r,L,R,k);
    pushup(x);
}
inline ll query(register int x,register int l,register int r,register int L,register int R)
{
    if(L<=l&&r<=R)
        return sum[x];
    if(tag[x])
        pushdown(x,l,r);
    ll res=0;
    int mid=l+r>>1;
    if(L<=mid)
        res+=query(x<<1,l,mid,L,R)%mod;
    if(R>=mid+1)
        res+=query(x<<1|1,mid+1,r,L,R)%mod;
    return res%mod;
}
inline void cal1(register int x,register int y,register int z)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy])
        {
            swap(x,y);
            swap(fx,fy);
        }
        update(1,1,tot,dl[fx],dl[x],z);
        x=fa[fx];
        fx=top[x];
    }
    if(dl[x]>dl[y])
        swap(x,y);
    update(1,1,tot,dl[x],dl[y],z);
}
inline ll cal2(register int x,register int y)
{
    ll res=0;
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy])
        {
            swap(x,y);
            swap(fx,fy);
        }
        res=(res%mod+query(1,1,tot,dl[fx],dl[x])%mod)%mod;
        x=fa[fx];
        fx=top[x];
    }
    if(dl[x]>dl[y])
        swap(x,y);
    res=(res%mod+query(1,1,tot,dl[x],dl[y])%mod)%mod;
    return res%mod;
}
int main()
{
    n=read(),m=read(),rt=read(),mod=read(); 
    for(register int i=1;i<=n;++i)
        ch[i]=read(),ch[i]%=mod;
    for(register int i=1;i<n;++i)
    {
        int u=read(),v=read();
        add(u,v),add(v,u);
    }
    dep[rt]=1;
    fa[rt]=rt;
    dfs1(rt);
    dfs2(rt,rt);
    build(1,1,n);
    while(m--)
    {
        int opt=read();
        if(opt==1)
        {
            int x=read(),y=read(),z=read();
            cal1(x,y,z%mod);
        }
        else if(opt==2)
        {
            int x=read(),y=read();
            write(cal2(x,y)%mod);
            printf("\n");
        }
        else if(opt==3)
        {
            int x=read(),z=read();
            update(1,1,tot,dl[x],dl[x]+size[x]-1,z%mod);
        }
        else
        {
            int x=read();
            write(query(1,1,tot,dl[x],dl[x]+size[x]-1)%mod);
            printf("\n");
        }
    }
    return 0;
}
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+'0');
}
const int N=1e5;
int v[N+10],dfn[N+10],Line[N+10];
int n,m,root,mod;
struct Segment{//线段树Lazy标记操作
    #define ls (p<<1)
    #define rs ((p<<1)|1)
    int tree[(N<<2)+10],Lazy[(N<<2)+10];
    void updata(int p){tree[p]=(tree[ls]+tree[rs])%mod;}
    void add_tag(int p,int l,int r,int v){
        tree[p]=(tree[p]+1ll*(r-l+1)*v)%mod;
        Lazy[p]=(Lazy[p]+v)%mod;
    }
    void pushdown(int p,int l,int r){
        if (!Lazy[p])   return;
        int mid=(l+r)>>1;
        add_tag(ls,l,mid,Lazy[p]),add_tag(rs,mid+1,r,Lazy[p]);
        Lazy[p]=0;
    }
    void build(int p,int l,int r){
        if (l==r){tree[p]=v[Line[l]]%mod;return;}
        int mid=(l+r)>>1;
        build(ls,l,mid),build(rs,mid+1,r);
        updata(p);
    }
    void insert(int p,int l,int r,int x,int y,int v){
        if (x<=l&&r<=y){add_tag(p,l,r,v);return;}
        int mid=(l+r)>>1;
        pushdown(p,l,r);
        if (x<=mid) insert(ls,l,mid,x,y,v);
        if (y>mid)  insert(rs,mid+1,r,x,y,v);
        updata(p);
    }
    int query(int p,int l,int r,int x,int y){
        if (x<=l&&r<=y) return tree[p];
        pushdown(p,l,r);
        int mid=(l+r)>>1,res=0;
        if (x<=mid) res=(res+query(ls,l,mid,x,y))%mod;
        if (y>mid)  res=(res+query(rs,mid+1,r,x,y))%mod;
        return res;
    }
}Tree;
struct Start{
    int pre[(N<<1)+10],child[(N<<1)+10],now[N+10];
    int deep[N+10],size[N+10],fa[N+10],Rem[N+10],top[N+10];
    int tot,cnt;
    void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    void build(int x,int Deep){//第一遍dfs
        deep[x]=Deep,size[x]=1;
        for (int p=now[x],son=child[p],Max=0;p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            fa[son]=x;
            build(son,Deep+1);
            size[x]+=size[son];
            if (Max<size[son])  Max=size[son],Rem[x]=son;
        }
    }
    void dfs(int x){//第二遍dfs
        if (!x) return;
        Rem[fa[x]]==x?top[x]=top[fa[x]]:top[x]=x;
        dfn[x]=++cnt,Line[cnt]=x;
        dfs(Rem[x]);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]||son==Rem[x])    continue;
            dfs(son);
        }
    }
    void insert(int x,int y,int z){//区间操作
        while (top[x]!=top[y]){
            if (deep[top[x]]<deep[top[y]])  swap(x,y);
            Tree.insert(1,1,n,dfn[top[x]],dfn[x],z);
            x=fa[top[x]];
        }
        if (deep[x]>deep[y])    swap(x,y);
        Tree.insert(1,1,n,dfn[x],dfn[y],z);
    }
    int query(int x,int y){//区间查询
        int res=0;
        while (top[x]!=top[y]){
            if (deep[top[x]]<deep[top[y]])  swap(x,y);
            res=(res+Tree.query(1,1,n,dfn[top[x]],dfn[x]))%mod;
            x=fa[top[x]];
        }
        if (deep[x]>deep[y])    swap(x,y);
        res=(res+Tree.query(1,1,n,dfn[x],dfn[y]))%mod;
        return res;
    }
}T;
int main(){
    n=read(),m=read(),root=read(),mod=read();
    for (int i=1;i<=n;i++)  v[i]=read();
    for (int i=1,x,y;i<n;i++)   x=read(),y=read(),T.join(x,y),T.join(y,x);
    T.build(root,1),T.dfs(root),Tree.build(1,1,n);
    for (int i=1;i<=m;i++){
        int t=read();
        if (t==1){
            int x=read(),y=read(),z=read();
            T.insert(x,y,z);
        }
        if (t==2){
            int x=read(),y=read();
            printf("%d\n",T.query(x,y));
        }
        if (t==3){//子树修改
            int x=read(),z=read();
            Tree.insert(1,1,n,dfn[x],dfn[x]+T.size[x]-1,z);
        }
        if (t==4){//子树查询
            int x=read();
            printf("%d\n",Tree.query(1,1,n,dfn[x],dfn[x]+T.size[x]-1));
        }
    }
    return 0;
}

 

posted @ 2019-10-06 08:52  Toot_Holmes  阅读(141)  评论(4)    收藏  举报