[Special topic] Intensive lecture on tree chain segmentation

What is tree chaining?

Refers to an algorithm for dividing a tree. It first divides the tree into multiple chains through light and heavy edge division to ensure that each point belongs to one and only one chain, and then passes the data structure (tree array, SBT, SPLAY, Line segment tree, etc.) to maintain each chain, mainly used to maintain the extreme value or sum of each chain on the tree.

similar

First, put the tree multiplication in the front. If you don't have tree multiplication, you don't have to look at the tree chain division.

Why learn tree chaining?

Some people say: "I will multiply on the tree, who am I afraid of?" Yes, you are afraid of tree chain division.
It means: what can be done by tree multiplication, can be done by tree chain division; what can be done by tree chain division, can not be done by tree multiplication. Although multiplication on the tree can find the extreme value or sum of the interval on the tree, once there is an operation to modify the weight of the upper edge or point of the tree, you will die. At this time, we need a new algorithm to supplement our multiplication on the tree. The loophole in the operation, yes, is the tree chain segmentation ! That's right! You will not regret learning it! !

Without further ado, here's the topic:

First, the concepts must be understood:

light son & heavy son

For a node, among all its children, the one with the largest size (subtree size) is the heavy son, and the rest are light sons. The tree chain in which the heavy son is located is called the heavy chain , and the rest are called the light chain .

write picture description here
There are 5 heavy chains in the figure, they are:
1--2--6--3
5
3
4--7
8.

What do we need to maintain?

  • tree array, representing the information on the entire tree
  • tree[i].depth represents the depth of i
  • tree[i].top represents the node at the top of the heavy chain where i is located
  • tree[i].father represents the father of point i
  • tree[i].key represents the weight of point i
  • tree[i].To_tree represents the position of node i in the segment tree
  • tree[i].size represents the size of the subtree rooted at i
  • tree[i].heavy_son represents the heavy son of the i node
  • To_num[i] indicates that the node i in the line segment tree corresponds to the position in the original tree
    //Since I am a konjac, here is the data structure line segment tree maintenance

    How to maintain?

    For the first dfs, we traverse the entire tree from the root, and then record the [depth, heavy_son, size, father, key] (dfs) of each point.

CODE:

int dfs(int t)
{
    int y,mx=0;
    tree[t].size=1;
    bz[t]=true;
    for (int i=last[t];i;i=next[i])
    {
        y=tov[i];
        if(!tree[y].dep)
        {
            tree[y].depth=tree[t].depth+1;
            tree[y].father=t;
            tree[y].key=len[i];
            dfs(y);
            tree[t].size+=tree[y].size;
            if(tree[y].size>mx)
            {
                mx=tree[y].size;
                tree[t].heavy_son=y;
            }
        }
    }
}

Artificial stack CODE:

void biuld_tree()
{
    int i,x,y;
    tot=1;
    d[1].t=root;tree[1].depth=1;
    for (i=1;i<=n;++i) cur[i]=last[i];
    while(tot)
    {
        x=d[tot].t;
        i=cur[x];
        while(tree[tov[i]].depth) i=next[i];
        if(!i)
        {
            d[tot--].maxx=0;
            ++tree[x].size;
            tree[d[tot].t].size+=tree[x].size;
            if(tree[x].size>d[tot].maxx) d[tot].maxx=tree[x].size,tree[d[tot].t].hs=x;
            continue;
        }
        cur[x]=next[i];
        y=tov[i];
        tree[y].fath=x;
        tree[y].depth=tree[x].depth+1;
        num[y]=number[i];
        d[++tot].t=y;       
    }       
}

operate:

  • build
  • Inquiries
    Inquiries include :
    LCA Inquiry
    Interval Answer Inquiry

build

The idea of ​​​​building a tree is dfs, each time the heavy son is searched first, to ensure that each point on the heavy chain is continuous in the position of the line segment tree. Maintain the [top, To_tree] and To_num of each point.

CODE:

void build_dfs(int t,int k)
{
    int y;
    bz[t]=true;
    tree[t].top=k;
    tree[t].To_tree=++tot;
    To_num[tot]=t;
    if(tree[t].heavy_son) dfs2(tree[t].heavy_son,k);
    for (int i=last[t];i;i=next[i])
    {
        y=tov[i];
        if(y!=tree[t].father&&y!=tree[t].heavy_son) build_dfs(y,y);
    }
}

Artificial stack CODE:

void complete_tree()
{
    int i,x,y;
    tot=1;sz=1;
    d[1].t=root;d[1].topp=root;
    tree[1].To_tree=1;tree[1].top=1;
    bz[0]=1;to_num[1]=1;
    for (i=1;i<=n;++i) cur[i]=last[i];
    while(tot)
    {
        x=d[tot].t;
        tree[x].top=d[tot].topp;
        bz[x]=true;
        if(!bz[tree[x].heavy_son])
        {
            tree[tree[x].heavy_son].top=d[tot].topp;
            d[++tot].t=tree[x].heavy_son;
            d[tot].topp=tree[tree[x].heavy_son].top;    
            tree[tree[x].heavy_son].To_tree=++sz;
            to_num[sz]=tree[x].heavy_son;
            continue;           
        }
        i=cur[x];
        while(bz[tov[i]]&&i) i=next[i];
        if(!i)
        {
            d[tot--].topp=0;
            continue;
        }
        cur[x]=next[i];
        y=tov[i];
        tree[tree[x].hs].top=d[tot].topp;
        tree[y].To_tree=++sz;
        to_num[sz]=y;
        d[++tot].t=y;d[tot].topp=y;     
    } 
}

Inquire

LCA query

Definitely simpler than tree-multiplied queries!
We query the LCA of x, y, using tree chaining.
STEPS :

  1. We check whether the current tree[x].top is equal to tree[y].top
  2. If they are not equal, then skip the one with the greater depth (let's say x) to the parent at the top of the current heavy chain.
  3. Iterate like this until they are top equal.
  4. If their tops are equal, there are two cases: ①x
    =y
    ②y is the ancestor of x (or x is the ancestor of y)

Then LCA is the one with less depth!
Isn't it easy~

CODE:

int query_LCA(int x,int y)
{
    while(tree[x].top!=tree[y].top)
    {
        if(tree[tree[x].top].depth>tree[tree[y].top].depth) x=tree[tree[x].top].father;
        else y=tree[tree[y].top].father;
    }
    return tree[x].depth<=tree[y].depth?x:y;
}

Interval extrema or sum query

Reasonable use of data structures such as line segment tree, in the process of finding LCA, the query of interval extreme value or sum is processed incidentally.

CODE:

int query_ans(int x,int y)
{
    int ans=INF;
    while(tree[x].top!=tree[y].top)
    {
        if(tree[tree[x].top].depth>tree[tree[y].top].depth) 
        {
            ans=min(ans,find(1,n,1,tree[tree[x].top].To_tree,tree[x].To_tree));
            x=tree[tree[x].top].father;
        }
        else
        {
            ans=min(ans,find(1,n,1,tree[tree[y].top].To_tree,tree[y].To_tree));
            y=tree[tree[y].top].father; 
        }
    }
    if(x==y) return ans;
    if(tree[x].depth<tree[y].depth) ans=min(ans,find(1,n,1,tree[tree[x].heavy_son].To_tree,tree[y].To_tree));
    else ans=min(ans,find(1,n,1,tree[tree[y].heavy_son].To_tree,tree[x].To_tree));
    return ans;
}

This CODE is the minimum value on the query chain

The basic knowledge is over, and the emphasis is on understanding. Do you understand?

Here's an example:

【NOIP2013 Improvement Team day1】Truck transportation

Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits

Description

Country A has n cities, numbered from 1 to n, and there are m two-way roads between the cities. Every road has a weight limit for vehicles, referred to as weight limit. There are now q trucks transporting goods, and drivers want to know how much each truck can carry without exceeding the vehicle's weight limit.

Input

The first line has two integers n and m separated by a space, indicating that A has n cities and m roads.
The next m lines each contain 3 integers x, y, and z, separated by a space, indicating that there is a road with a weight limit of z from city x to city y. Note: x is not equal to y, there may be multiple roads between two cities.
The next line has an integer q, indicating that there are q trucks that need to be transported.
Next q lines, each line contains two integers x and y, separated by a space, indicating that a truck needs to transport goods from city x to city y. Note: x is not equal to y.

Output

The output consists of q lines, each with an integer representing the maximum load for each truck. If the truck cannot reach the destination, output -1.

Sample Input

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

Sample Output

3
-1
3

Data Constraint

For 30% of the data, 0 < n < 1,000, 0 < m < 10,000, 0 < q < 1,000;
for 60% of the data, 0 < n < 1,000, 0 < m < 50,000, 0 < q < 1,000;
for 100 % of data, 0 < n < 10,000, 0 < m < 50,000, 0 < q < 30,000, 0 ≤ z ≤ 100,000.

Give some time to think~
Consider~
We are talking about tree chain segmentation, think about how to hook it
up ~
Have any ideas?

Okay, let's get right to the point:

Obviously, first of all, we need to do a maximum spanning tree, which is used to facilitate us to find the maximum value of the minimum value~ This is no problem.
Then there are two choices. Because there is no weight modification, it is also possible to use multiplication directly.
Of course, we are now talking about tree chain segmentation.
If you just understood it, now should know how the tree should be cut~

CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct Moon{int top,To_tree,heavy_son,father,depth,size;long long key;};
struct Candy{int x,y,z;};
int father[10001];
Moon tree[10001];
Candy a[50001];
int n,m,q,tot;
int To_num[1000001];
int next[50001],last[50001],len[50001],tov[50001];
long long f[1000001];
bool bz[10001];
bool cmp(Candy a,Candy b){return a.z>b.z;}
void insert(int x,int y,int z)
{
    tov[++tot]=y;
    len[tot]=z;
    next[tot]=last[x];
    last[x]=tot;
}
int getfather(int x)
{
    if(father[x]==x) return x;
    father[x]=getfather(father[x]);
    return father[x];
}
void dfs(int t)
{
    int y,mx=0;
    tree[t].size=1;
    bz[t]=true;
    for (int i=last[t];i;i=next[i])
    {
        y=tov[i];
        if(!tree[y].depth)
        {
            tree[y].depth=tree[t].depth+1;
            tree[y].father=t;
            tree[y].key=len[i];
            dfs(y);
            tree[t].size+=tree[y].size;
            if(tree[y].size>mx)
            {
                mx=tree[y].size;
                tree[t].heavy_son=y;
            }
        }
    }
}
void dfs2(int t,int k)
{
    int y;
    bz[t]=true;
    tree[t].top=k;
    tree[t].To_tree=++tot;
    To_num[tot]=t;
    if(tree[t].heavy_son)dfs2(tree[t].heavy_son,k);
    for (int i=last[t];i;i=next[i])
    {
        y=tov[i];
        if(y!=tree[t].father&&y!=tree[t].heavy_son) dfs2(y,y);
    }
}
void buildtree(int l,int r,int v)
{
    if(l==r)
    {
        f[v]=tree[To_num[l]].key;
        return;
    }
    int mid=(l+r)/2;
    buildtree(l,mid,v*2);
    buildtree(mid+1,r,v*2+1);
    f[v]=min(f[v*2],f[v*2+1]);
}
long long find(int l,int r,int v,int x,int y)
{
    if(l==x&&r==y) return f[v];
    int mid=(l+r)/2;
    if(y<=mid) return find(l,mid,v*2,x,y);
    else if(x>mid) return find(mid+1,r,v*2+1,x,y);
    else return min(find(l,mid,v*2,x,mid),find(mid+1,r,v*2+1,mid+1,y));
}
long long query_ans(int x,int y)
{
    long long ans=9187201950435737471;
    while(tree[x].top!=tree[y].top)
    {
        if(tree[tree[x].top].depth>tree[tree[y].top].depth) 
        {
            ans=min(ans,find(1,n,1,tree[tree[x].top].To_tree,tree[x].To_tree));
            x=tree[tree[x].top].father;
        }
        else
        {
            ans=min(ans,find(1,n,1,tree[tree[y].top].To_tree,tree[y].To_tree));
            y=tree[tree[y].top].father; 
        }
    }
    if(x==y) return ans;
    if(tree[x].depth<tree[y].depth) ans=min(ans,find(1,n,1,tree[tree[x].heavy_son].To_tree,tree[y].To_tree));
    else ans=min(ans,find(1,n,1,tree[tree[y].heavy_son].To_tree,tree[x].To_tree));
    return ans;
}
int main()
{
    freopen("truck.in","r",stdin);
    freopen("truck.out","w",stdout);
    scanf("%d%d",&n,&m);
    int i,j,xx,yy;
    for (i=1;i<=n;++i)father[i]=i;
    for (i=1;i<=m;++i)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a+1,a+1+m,cmp);
    for (i=1;i<=m;++i)
    {
        xx=getfather(a[i].x);   
        yy=getfather(a[i].y);
        if(xx!=yy) 
        {
            father[xx]=yy;
            insert(a[i].x,a[i].y,a[i].z);
            insert(a[i].y,a[i].x,a[i].z);
        }
    }
    tree[1].depth=1;
    tot=0;
    for (int i=1;i<=n;i++)
        if (!bz[i])
        {
            tree[i].depth=1;
            tree[i].father=0;
            dfs(i),dfs2(i,i);
        }
    memset(f,127,sizeof(f));
    buildtree(1,n,1);
    scanf("%d",&q);
    for (i=1;i<=q;++i)
    {
        scanf("%d%d",&xx,&yy);
        if(getfather(xx)!=getfather(yy)) printf("-1\n");
        else printf("%lld\n",query_ans(xx,yy));
    }
} 

A few more examples are recommended:
JZOJsenior2753. [2012 Dongguan City Selection] Tree (tree)
[ZJOI2008] Statistics
of Trees Statistics of trees CODE:

#include<cstdio>
#include<algorithm>
#define MAXN 30005
#define INF 1<<30
using namespace std;
int l[MAXN],r[MAXN];
struct node{int sum,max;};
node tree[MAXN*4];     
struct Edge{int v,next;};//v:value
Edge edge[MAXN*2];
int head[MAXN],now,val[MAXN],link[MAXN],dep[MAXN],fa[MAXN],sonTree[MAXN],heavySon[MAXN],tot,top[MAXN],num[MAXN];     
int n,u,v,q;
void addEdge(int u,int v)
{
    now++;
    edge[now].v=v;
    edge[now].next=head[u];
    head[u]=now;
}
void buildTree(int now,int l,int r)
{
    if (l==r) 
    {
        tree[now].max=val[link[l]];//link(position)
        tree[now].sum=val[link[l]];
        return;
    }
    int mid=(l+r)/2;
    buildTree(now*2,l,mid);
    buildTree(now*2+1,mid+1,r);
    tree[now].max=max(tree[now*2].max,tree[now*2+1].max);
    tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
}

void DFS1(int now,int nowFa,int nowDep)
//HeavySon,Dep,SonTree,Father
{
    dep[now]=nowDep;fa[now]=nowFa;sonTree[now]=1;
    for (int x=head[now];x!=0;x=edge[x].next)
    {
        if(edge[x].v==nowFa) continue;//bz
        DFS1(edge[x].v,now,nowDep+1);
        sonTree[now]+=sonTree[edge[x].v];
        if(heavySon[now]==0||sonTree[edge[x].v]>sonTree[heavySon[now]]) heavySon[now]=edge[x].v;
    }
}
void DFS2(int now,int nowTop)
//num:The numbering after this point is split 
//link:The position of the current node in the segment tree
{
    tot++;
    top[now]=nowTop; num[now]=tot; link[tot]=now;
    if (heavySon[now]==0) return;
    DFS2(heavySon[now],nowTop);
    for (int x=head[now];x!=0;x=edge[x].next)
        if(edge[x].v!=heavySon[now]&&edge[x].v!=fa[now]) DFS2(edge[x].v,edge[x].v);
}

void init()
{
    scanf("%d",&n);
    for (int i=1;i<=n-1;i++) 
    {
        scanf("%d %d",&u,&v);
        addEdge(u,v),addEdge(v,u);
    } 
    for (int i=1;i<=n;i++) scanf("%d",&val[i]);
    DFS1(1,0,1); DFS2(1,1);
    buildTree(1,1,n);
}

void update1(int now,int l,int r,int loc,int delta)
{
    if (l==r)
    {
        tree[now].sum+=delta;
        tree[now].max+=delta;
        return;
    }
    int mid=(l+r)/2;
    if (loc<=mid) update1(now*2,l,mid,loc,delta);
    else update1(now*2+1,mid+1,r,loc,delta);
    tree[now].max=max(tree[now*2].max,tree[now*2+1].max);
    tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
}

int query1(int now,int l,int r,int ql,int qr)
{
    int ans=0;
    if (ql<=l && r<=qr) return tree[now].sum;
    int mid=(l+r)/2;
    if (ql<=mid) ans+=query1(now*2,l,mid,ql,qr);
    if (qr>mid) ans+=query1(now*2+1,mid+1,r,ql,qr);
    return ans;
}

int query2(int now,int l,int r,int ql,int qr)
{
    int ans=-INF;
    if (ql<=l && r<=qr) return tree[now].max;
    int mid=(l+r)/2;
    if (ql<=mid) ans=max(ans,query2(now*2,l,mid,ql,qr));
    if (qr>mid) ans=max(ans,query2(now*2+1,mid+1,r,ql,qr));
    return ans;
}

int getMax(int l,int r)
{
    int f1=top[l],f2=top[r],ans=-INF,nowAns;
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        ans=max(ans,query2(1,1,n,num[f1],num[l]));
        l=fa[f1]; f1=top[l];
    }
    if (dep[l]>dep[r]) nowAns=query2(1,1,n,num[r],num[l]);
    else nowAns=query2(1,1,n,num[l],num[r]);
    ans=max(ans,nowAns);
    return ans;
}

int getSum(int l,int r)
{
    int f1=top[l],f2=top[r],ans=0,nowAns;
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        ans+=query1(1,1,n,num[f1],num[l]);
        l=fa[f1]; f1=top[l];
    }
    if (dep[l]>dep[r]) nowAns=query1(1,1,n,num[r],num[l]);
    else nowAns=query1(1,1,n,num[l],num[r]);
    ans+=nowAns;
    return ans;       
}

int main()
{
    char s[7];
    init();
    scanf("%d",&q);
    for (int i=1;i<=q;i++) 
    {
        scanf("%s %d %d",s,&u,&v);
        if (s[0]=='C') update1(1,1,n,num[u],v-val[u]),val[u]=v;//CHANGE
        else if (s[1]=='M') printf("%d\n",getMax(u,v));//QMAX
        else printf("%d\n",getSum(u,v));//QSUM
    }
    return 0;
 }

After completing these questions, you will be the leader of tree chain dissection~

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326046086&siteId=291194637