Chain split tree review notes

Dove for a long time a blog ah ....

Title Description

As stated, it is known a tree comprising N nodes (communication with and without a ring), each node comprising a numerical value, needs to support the following operations:

Operation 1: Format: 1 shows a tree XYZ values ​​of all the nodes on the shortest path from the node x to y plus z are

Operation 2: Format: 2 XY indicates finding a tree node from x to y value on the shortest path, and all nodes

Operation 3: Format: 3 x will be represented by an xz all nodes within the subtree root values ​​are plus z

Operation 4: Format: 4 x values ​​represent all nodes within the subtree root node seeking and at x

Input and output formats

Input formats:

 

The first row contains four positive integers N, M, R, P, respectively represent the number of tree nodes, the number of operations, and the number modulo the number of the root node (i.e., all of this output are modulo).

The next line contains N non-negative integers, it represents an initial value sequentially, on each node.

Then N-1 lines contains two integers x, y, represents an edge even with a (ring and to ensure that no communication) between point x and point y

Next M lines each comprising a number of positive integers, each row represents an operation, the following format:

Operation 1: 1 xyz

Operation 2: 2 xy

Operation 3: 3 xz

Operation 4: 4 x

 

Output formats:

 

Comprising a plurality of output lines, respectively, each operation sequentially represents 2 or 4 operation results obtained (p modulo P)

 

Sample input and output

Input Sample # 1: 
5 5 2 24
7 3 7 8 0 
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3
Output Sample # 1: 
2
21

Explanation

Time and space constraints: 1s, 128M

Data Scale:

For 30% of the data:  N . 1 0 , M . 1 0

For 70% of the data:  N . 1 0 . 3 , M . 1 0 . 3

To 100% of the data:  N . 1 0 . 5 , M . 1 0 . 5

( In fact, pure random generated tree LCA + violence is able to live, but you think it may be purely random 233 )

Sample Description:

Tree as follows:

 Each operates as follows:

(Before always felt very difficult to cross the tree ...)

Chain split tree, the tree is divided into their chain, and then maintaining the value of a data structure.

Overall difficulty is two dfs.

First, the principle

The tree is divided into a chain and a light chain, and maintain a data structure.

Second, the concept of several groups

 Heavy side: even the edge of his heavy parent and son

      Heavy Son: For non-leaf node, and his son to the son as the root of sub-tree nodes eldest son as the most important son

      Light Edge: In addition to the heavy side, the rest are light side

      Light Son: each son in the non-leaf nodes, remove heavy son, and the rest are all sons of light

      Heavy chain: When the weight of a full side chain composition, which is a heavy chain.

      note:

  • For the leaf node, if it is the son of light, there is a starting point to themselves as the length of a chain.
  • Each heavy chain are lighter son as a starting point, that is mentioned below TOP.
  • Neither the son of leaf nodes heavy nor light son, because he had no son. . .
  • The value of each edge is actually carried out when the number of DFS.

     Figure

why? Heavy chain, we certainly hope to maintain the more simple things more close together, so the longer the chain we put them together pigeons (Gugu Gu)

Then they use treap segment tree ah ah like to maintain just fine.

Third, the means of achieving

(1) declare variables

struct edge
{
    int to,next;
}e[maxn];
int head[maxn],cnt;
inline int addedge(int from,int to)//初步存图(树)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
int rt=0;
int son[maxn];//节点的重儿子 
int size[maxn];//子树大小 
int top[maxn];//重链的顶端 
int dep[maxn];//深度
int dfsn[maxn];//dfs序 
int fa[maxn];//点的父亲 
int newid[maxn];//新的点在线段树里的下标 
int tot;//节点数量
struct tree//线段树
{
    int l,r,ls,rs,pre,add;
}t[maxn];

 

要实现标记轻儿子,重儿子,我们需要子树大小,这需要dfs,深度,这也要dfs,dfs序....反正就是dfs就是了

所以

(2)dfs

void dfs1(int u)//处理的量:子树大小,深度,fa,son
{
    size[u]=1;//首先子树大小为1(自身)
    dep[u]=dep[fa[u]]+1;//同lca预处理
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=fa[u])//向下遍历
        {
            fa[v]=u;
            dfs1(v);//先向下
            size[u]+=size[v];//再统计子树大小
            if(size[son[u]]<size[v])////////////////
            son[u]=v;//保存重儿子
        }
    }
}

dfs1还是较好理解&&实现的跑完这样:

 

个人认为难点是dfs2

void dfs2(int u,int d)//当前点,和链顶//处理量:链顶,dfs序,新编号
{
    top[u]=d;
    dfsn[u]=++tot;//记录dfs序
    newid[tot]=u;//线段树里的东西
    if(son[u])//如果有重儿子
    dfs2(son[u],d);//继续走重儿子伸长重链
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])//如果v是轻儿子
        dfs2(v,v);//那跟新链顶,继续向下
    }
}

其实到这树剖就已经差不多了跑完这样:

 

(3)数据结构

以线段树为例:

inline void pushup(int p)
{
    t[p].pre=(t[t[p].ls].pre+t[t[p].rs].pre)%mod;//向下跟新值
}

void build(int l,int r,int p)//普普通通的建树
{
    if(l==r)
    {
        t[p].pre=a[newid[l]];
        t[p].l=t[p].r=l;//
        return ;
    }
    int mid=l+r>>1;
    t[p].ls=tot++;
    t[p].rs=tot++;
    build(l,mid,t[p].ls);
    build(mid+1,r,t[p].rs);
    t[p].l=t[t[p].ls].l;//坑点在这里,普通线段树可以用<<1|1什么的找到儿子,但是因为树剖的特殊性,这里不行(个人认为这样要更方便)
    t[p].r=t[t[p].rs].r;
    pushup(p);//加值
}
inline int len(int p)
{
    return t[p].r-t[p].l+1;
}
void spread(int p)
{
    if(t[p].add)//下传标记
    {
        int ls=t[p].ls,rs=t[p].rs,lz=t[p].add;
        t[ls].add=(t[ls].add+lz)%mod;
        t[rs].add=(t[rs].add+lz)%mod;
        t[ls].pre=(t[ls].pre+lz*len(ls))%mod;
        t[rs].pre=(t[rs].pre+lz*len(rs))%mod;
        t[p].add=0;
    }
}
void change(int l,int r,int k,int p)//普普通通的区间加
{
    if(l<=t[p].l&&r>=t[p].r)
    {
        t[p].pre=(t[p].pre+k*(t[p].r-t[p].l+1))%mod;
        t[p].add=(t[p].add+k)%mod;
        return;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(l<=mid)change(l,r,k,t[p].ls);
    if(r>mid) change(l,r,k,t[p].rs);
    pushup(p);
}
int ask(int l,int r,int p)//普普通通的区间查
{
    if(t[p].l>=l&&t[p].r<=r)
        return t[p].pre%mod;
    spread(p);
    int mid=t[p].l+t[p].r>>1,res=0;
    if(mid>=l)res=(res+ask(l,r,t[p].ls))%mod;
    if(mid<r) res=(res+ask(l,r,t[p].rs))%mod;
    return res%mod;
}

 这样就处理好了第三第四操作~

下面,又是一个难点:

操作1和操作2.这里的处理方式有点像lca....

inline int sum(int x,int y)
{
    int ret=0;
    while(top[x]!=top[y])//一直走到顶
    {
        if(dep[top[x]]<dep[top[y]])
        swap(x,y);
        ret=(ret+ask(dfsn[top[x]],dfsn[x],rt)%mod);//可以区间查的区间查
        x=fa[top[x]];//跳上去
    }
//循环之后,这两个点在同一重链上,但由于不知道是否是同一点,所以来统计一下两点的贡献
if(dfsn[x]>dfsn[y]) swap(x,y); return (ret+ask(dfsn[x],dfsn[y],rt))%mod; } inline void updates(int x,int y,int c) { while(top[x]!=top[y])//同上,能区间加的区间加 { if(dep[top[x]]<dep[top[y]]) swap(x,y); change(dfsn[top[x]],dfsn[x],c,rt); x=fa[top[x]];//跳上去 } if(dfsn[x]>dfsn[y]) swap(x,y); change(dfsn[x],dfsn[y],c,rt); }

于是,树剖基本就结束了(真的结束了)

完整代码(模板题):

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e6+10;
int n,m,r,mod;
int a[maxn];
struct edge
{
    int to,next;
}e[maxn];
int head[maxn],cnt;
inline int addedge(int from,int to)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
int rt=0;
int son[maxn];//节点的儿子 
int size[maxn];//子树大小 
int top[maxn];//重链的顶端 
int dep[maxn];//深度
int dfsn[maxn];//dfs序 
int fa[maxn];//点的父亲 
int newid[maxn];//新的点在线段树里的下标 
int tot;//|ì??ਤo? 
struct tree
{
    int l,r,ls,rs,pre,add;
}t[maxn];
void dfs1(int u)
{
    size[u]=1;
    dep[u]=dep[fa[u]]+1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=fa[u])
        {
            fa[v]=u;
            dfs1(v);
            size[u]+=size[v];
            if(size[son[u]]<size[v])////////////////
            son[u]=v;
        }
    }
}
void dfs2(int u,int d)
{
    top[u]=d;
    dfsn[u]=++tot;
    newid[tot]=u;
    if(son[u])
    dfs2(son[u],d);
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=son[u]&&v!=fa[u])
        dfs2(v,v);
    }
}

inline void pushup(int p)
{
    t[p].pre=(t[t[p].ls].pre+t[t[p].rs].pre)%mod;
}

void build(int l,int r,int p)
{
    if(l==r)
    {
        t[p].pre=a[newid[l]];
        t[p].l=t[p].r=l;
        return ;
    }
    int mid=l+r>>1;
    t[p].ls=tot++;
    t[p].rs=tot++;
    build(l,mid,t[p].ls);
    build(mid+1,r,t[p].rs);
    t[p].l=t[t[p].ls].l;
    t[p].r=t[t[p].rs].r;
    pushup(p);
}
inline int len(int p)
{
    return t[p].r-t[p].l+1;
}
void spread(int p)
{
    if(t[p].add)
    {
        int ls=t[p].ls,rs=t[p].rs,lz=t[p].add;
        t[ls].add=(t[ls].add+lz)%mod;
        t[rs].add=(t[rs].add+lz)%mod;
        t[ls].pre=(t[ls].pre+lz*len(ls))%mod;
        t[rs].pre=(t[rs].pre+lz*len(rs))%mod;
        t[p].add=0;
    }
}
void change(int l,int r,int k,int p)
{
    if(l<=t[p].l&&r>=t[p].r)
    {
        t[p].pre=(t[p].pre+k*(t[p].r-t[p].l+1))%mod;
        t[p].add=(t[p].add+k)%mod;
        return;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(l<=mid)change(l,r,k,t[p].ls);
    if(r>mid) change(l,r,k,t[p].rs);
    pushup(p);
}
int ask(int l,int r,int p)
{
    if(t[p].l>=l&&t[p].r<=r)
        return t[p].pre%mod;
    spread(p);
    int mid=t[p].l+t[p].r>>1,res=0;
    if(mid>=l)res=(res+ask(l,r,t[p].ls))%mod;
    if(mid<r) res=(res+ask(l,r,t[p].rs))%mod;
    return res%mod;
}
inline int sum(int x,int y)
{
    int ret=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        swap(x,y);
        ret=(ret+ask(dfsn[top[x]],dfsn[x],rt)%mod);
        x=fa[top[x]];
    }
    if(dfsn[x]>dfsn[y])
    swap(x,y);
    return (ret+ask(dfsn[x],dfsn[y],rt))%mod;
}
inline void updates(int x,int y,int c)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        swap(x,y);
        change(dfsn[top[x]],dfsn[x],c,rt);
        x=fa[top[x]];
    }
    if(dfsn[x]>dfsn[y])
    swap(x,y);
    change(dfsn[x],dfsn[y],c,rt);
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&mod);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs1(r);
    dfs2(r,r);
    tot=0;
    build(1,n,rt=tot++);
    for(int i=1;i<=m;i++)
    {
        int flag,x,y,z;
        scanf("%d",&flag);
        if(flag==1)
        {
            scanf("%d%d%d",&x,&y,&z);
            updates(x,y,z);
        }
        if(flag==2)
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",sum(x,y)%mod);
        }
        if(flag==3)
        {
            scanf("%d%d",&x,&z);
            change(dfsn[x],dfsn[x]+size[x]-1,z,rt);
        }
        if(flag==4)
        {
            scanf("%d",&x);
            printf("%d\n",ask(dfsn[x],dfsn[x]+size[x]-1,rt)%mod);
        }
    }
    return 0;
}

 (完)

图片来源:https://www.cnblogs.com/2529102757ab/p/10732188.html

Guess you like

Origin www.cnblogs.com/ajmddzp/p/11313562.html