[hdu6547] Tree (Tree chain division, segment Tree interval root number)

problem

algorihtm

1. Tree chain subdivision

What is tree chain subdivision (heavy chain subdivision)?

  • Tree chain is the path on the tree. Subdivision is to classify paths into heavy chains and light chains.
  • For a point on a tree, among the connected edges, the edge with the largest size (heavy son) of the connected node subtree is called the heavy edge, and the other edges are called the light edge. The edges that are connected by multiple edges are called heavy chains.
  • The black bold chain in the figure below is a heavy chain.

How to realize tree chain subdivision (heavy chain subdivision)?

  • The implementation of heavy chain subdivision is realized by twice dfs. For the first time, dfs processes the heavy son [] of each point, the size [] of the subtree, the depth d [] and the parent node f []. The second pass of dfs is to process the chain head top [] of the heavy chain where each point is located
  • The specific implementation is very simple. During backtracking, the size relationship between the current child node and the heavy son subtree is directly compared to update the heavy son. After finding the heavy son, recurse again. Maintain the current chain top each time. If it is a light edge, replace the current node as the chain top.

What are the properties of tree chain partition (heavy chain partition)?

  • Property 1: all heavy chains do not intersect each other, that is, each point belongs to only one heavy chain
  • Property 2: the number of light edges and heavy edges on the path from a point to the root node does not exceed log

Proof of property 2: because the worst case is that the edge from this point to the root path is a light edge, every time a light edge reaches the parent node of this point, it means that the parent node has at least one subtree as large as the current subtree, that is, the subtree size of the point reached by each light edge must be * 2, so it can only go log times at most. This is why we should choose our son instead of any son.

What is the use of tree chain subdivision (heavy chain subdivision)?
Heavy chain splitting can ensure that the DFS order of nodes on each divided chain is continuous, so it is convenient to use some data structures of maintenance sequence (such as segment tree) to maintain the path information on the tree:

  • Modify the values of all points on the path between two points on the tree
  • Sum / extremum / other of node weights on the path between two points in the query tree

What is DFS sequence?

  • The time series of each node in and out of the stack in dfs depth first traversal.
  • We find that the time period between the time points of a point in and out of the stack is all the time of its subtree in the stack.
  • In other words, the dfs order (timestamp) of the subtree must be greater than the stack time of the root node and less than the stack time of the root node, which becomes an interval problem. So we "shoot" a tree problem to a linear data structure.

What is a timestamp?

  • The concept of timestamp is: according to the depth first traversal process, each node is marked with 1 − N in order of being accessed for the first time. This mark is a timestamp.
  • Timestamp is the reverse mapping of DFS order:
    id[i]=x, I is the time, X is the node, and id is the DFS sequence. That is, the point corresponding to time (tarjan)
    dfn[x]=i, i is the time, X is the node, and dfn is the timestamp. That is, the time corresponding to the node (tree section)
  • Many people on the Internet say that the DFS sequence is used for tree chain segmentation. Strictly speaking, this statement is not very rigorous. In fact, it realizes the transformation of the tree to a continuous interval by maintaining the timestamp.

2. Segment tree interval root

  • For the original two numbers a and B (a > b), after root opening, they become √ A and √ B, and their difference changes from a − B to √ a − √ B.
  • Consider maintaining an interval maximum value MAX and an interval minimum value MIN. if MAX - √ MAX==MIN - √ MIN, that is, the difference after root opening of all numbers in the interval is the same, the interval root opening operation can be converted to interval subtraction.

solution

//Given a tree, q operations. Each time, you can ask the weight sum of the shortest path point between any two points in the tree, or root each weight of the shortest path point between any two points in the tree.
//Idea: modify / query the values of all points on the path between two points in the tree and the tree chain partition template. The interval open root can be maintained by the segment tree.
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;

//subject
int n, q;
LL a[maxn];//Value of each node in the tree
vector<int>G[maxn];

//Tree chain subdivision
int son[maxn], dad[maxn], siz[maxn], dep[maxn];
void dfs1(int x, int fa){//Deal with heavy son, father, depth, subtree size
    son[x] = 0;  dad[x] = fa;
    siz[x] = 1;  dep[x] = dep[fa]+1;
	for(int to : G[x]){
		if(to==fa)continue;
		dfs1(to,x);
		siz[x] += siz[to];
		if(siz[to]>siz[son[x]])son[x]=to;
	}
}
int top[maxn], pos[maxn], tot=0;//At the top of the chain, timestamp number
LL v[maxn];//Mapped to the value after the sequence, the segment tree is built according to this sequence
void dfs2(int x, int fa, int k){//Process the chain top, timestamp and mapped value
    top[x] = k;
    pos[x] = ++tot;
    v[tot] = a[x];
    if(son[x])dfs2(son[x], x, k);//The heavy chain goes down without updating the chain top
    for(int to : G[x]){
        if(to==fa)continue;
        if(to==son[x])continue;
        dfs2(to, x, to);//Update to top of chain
    }
}

//Segment tree
#define lch p<<1
#define rch p<<1|1
struct node{
    int l, r;
    LL mx, mi, sum, lazy;//Maintenance interval plus interval max,min
}sgt[maxn<<2];
void pushdown(int p){
    sgt[lch].lazy += sgt[p].lazy;
    sgt[rch].lazy += sgt[p].lazy;
    sgt[lch].sum += (sgt[lch].r-sgt[lch].l+1)*sgt[p].lazy;
    sgt[rch].sum += (sgt[rch].r-sgt[rch].l+1)*sgt[p].lazy;
    sgt[lch].mx += sgt[p].lazy;
    sgt[lch].mi += sgt[p].lazy;
    sgt[rch].mx += sgt[p].lazy;
    sgt[rch].mi += sgt[p].lazy;
    sgt[p].lazy = 0;
}
void pushup(int p){
    sgt[p].sum = sgt[lch].sum+sgt[rch].sum;
    sgt[p].mx = max(sgt[lch].mx, sgt[rch].mx);
    sgt[p].mi = min(sgt[lch].mi, sgt[rch].mi);
}
void build(int p, int l, int r){
    sgt[p].l = l;  sgt[p].r = r;
    if(l==r){
        sgt[p].mx = sgt[p].mi = sgt[p].sum = v[l];
        return ;
    }
    int mid = l+r>>1;
    build(lch, l, mid);
    build(rch, mid+1, r);
    pushup(p);
}
LL querysum(int p, int l, int r){
    if(sgt[p].l==l && sgt[p].r==r)return sgt[p].sum;
    if(sgt[p].lazy)pushdown(p);
    int mid = (sgt[p].l+sgt[p].r)>>1;
    if(r<=mid)return querysum(lch, l, r);
    else if(l>mid)return querysum(rch, l, r);
    else return querysum(lch, l, mid)+querysum(rch, mid+1,r);
}
void update(int p, int l, int r){
    if(sgt[p].l==l && sgt[p].r==r){
        LL a = sqrt(sgt[p].mx), b = sqrt(sgt[p].mi);
        if(sgt[p].mx==sgt[p].mi || sgt[p].mx-a==sgt[p].mi-b){//The reduced value after root opening is the same
            LL v= sgt[p].mx-a;//It will reduce so much after root opening
            sgt[p].sum -= (r-l+1)*v;
            sgt[p].mx -= v;
            sgt[p].mi -= v;
            sgt[p].lazy -= v;
            return ;
        }
    }
    if(sgt[p].lazy)pushdown(p);
    int mid = (sgt[p].l+sgt[p].r)>>1;
    if(r <= mid)update(lch, l, r);
    else if(l > mid)update(rch, l, r);
    else update(lch, l, mid), update(rch, mid+1, r);
    pushup(p);
}

//subject
LL calsum(int u, int v){//Calculate [u,v] paths and
    LL res = 0;
    while(top[u]!=top[v]){//Not on the same chain, climb up at the point with greater depth at the top of the chain each time
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        res += querysum(1,pos[top[u]], pos[u]); //sum[pos[top[u]], pos[u]]
        u = dad[top[u]]; //Enter a new chain
    }
    if(dep[u]>dep[v])swap(u,v);//When entering the same chain, find it again. The points with small interval and depth are in front
    res += querysum(1, pos[u], pos[v]);
    return res;
}
void updatework(int u, int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        update(1,pos[top[u]], pos[u]);
        u = dad[top[u]]; 
    }
    if(dep[u]>dep[v])swap(u,v);
    update(1,pos[u], pos[v]);
}

int main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin>>n>>q;
    for(int i = 1; i <= n; i++)cin>>a[i];
    for(int i = 1; i < n; i++){
        int u, v;  cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,0);
    dfs2(1,0,1);
    build(1,1,n);
    //for(int i = 1 ; i<= n; i++)cout<<top[i]<<" "; cout<<"\n"; return 0;
    for(int i = 1; i <= q; i++){
        int op, u, v;  cin>>op>>u>>v;
        if(op==0)updatework(u,v);
        else cout<<calsum(u,v)<<"\n";
    }
    return 0;
}

Tags: data structure neural networks Deep Learning

Posted on Mon, 18 Oct 2021 15:28:45 -0400 by harris97