[Pin to the top] tree link partitioning Section

Source: Internet
Author: User

I learned how to split down the tree chain some time ago. I haven't looked at it for a long time. I'll review it again today. Hurry up and write it down. Don't forget it.

In the informatics competition, we sometimes encounter such a type of question. In a tree, we modify a variable (such as the length of the edge) on all the edges (or vertices) in the path between two points, and then ask about some properties (such as the sum of edge weights) of all vertices (or edges) in a single vertex (or edge) or path between two points, maximum side, Minimum side, etc ). For such questions, it is often easy to lean to the line segment tree. However, simply using the line segment tree cannot maintain the nature of each chain, therefore, we need an algorithm to split the tree chain so that each chain can correspond to one interval in the line tree. (Of course, tree chain partitioning is far more than just these simple applications, and it is not necessarily related to the line tree. In short, it is to split the tree chain ).

There are many methods to split tree links. The most common method is to split the light and heavy sides (most of the content on the Internet is about this method). What is the light and heavy sides?

We first divide the edges in the tree into two parts: the light and the heavy edges. Remember that size (u) is the number of nodes in the Child tree rooted in U, so that V is the largest of the son of U (if there are multiple largest, only take one), then we say that the side (u, v) is the most important side, the remaining edges are light edges (as shown in red, heavy edges, and blue ).


After dividing all the edges of a tree into light and heavy edges based on the preceding method, we can obtain the following attributes:

1: If (u, v) is a light edge, size (v) <= size (u)/2.

This is obvious.

2: The number of light edges on the path from the root to a certain point does not exceed O (logn) (N is the total number of nodes ).

This is also very simple, because assuming there are K light edges in the path from root to V, they are root->... -> V1->... -> v2-> ...... -> VK->... -> V, we set size (v) = num, apparently num> = 1, then by Nature 1, we have size (VK)> = 2, size (Vk-1)> = 4 ...... size (V1)> = 2 ^ K, obviously there are 2 ^ k <= N, So k <= log2 (n ).


If we connect consecutive duplicate edges in a chain to form a heavy chain, a chain becomes a chain of alternating segments of the light and heavy chains, and the number of segments is log (n) level, we can talk about the maintenance of heavy chains in the line segment tree. The light side can be placed or not. For convenience, I usually put them, but the speed will be discounted. There are so many ideas, and the next step is implementation.

We need to maintain the following values:

Siz [v] indicates the total number of nodes in the subtree rooted in v.

Dep [v] indicates the depth of v.

Son [v] indicates the son node of V on the same heavy chain as v.

Fa [v] indicates V's Father's Day.

Top [v] indicates the top node of the chain where v is located.

W [v] indicates the position in the online segment tree of node v.

Siz [], son [], Fa [], DEP [] can be obtained in the first DFS, top [], W [] can be obtained in the second DFS. See the code for the specific process.

 

struct edge{    int to;    int next;}e[maxn<<1];int box[maxn],cnt,tot;void init(){    tot=0;    son[0]=dep[0]=0;    memset(box,-1,sizeof(box));    cnt=0;}void add(int from,int to){    e[cnt].to=to;    e[cnt].next=box[from];    box[from]=cnt++;}int siz[maxn],top[maxn],son[maxn],dep[maxn],w[maxn],fa[maxn];void dfs(int now,int pre){    siz[now]=1;    fa[now]=pre;    son[now]=0;    dep[now]=dep[pre]+1;    int t,v;    for(t=box[now];t+1;t=e[t].next)    {        v=e[t].to;        if(v!=pre)        {            dfs(v,now);            siz[now]+=siz[v];            if(siz[son[now]]<siz[v])            {                son[now]=v;            }        }    }}void dfs2(int now,int tp){    w[now]=++tot;    top[now]=tp;    if(son[now])    dfs2(son[now],top[now]);    int t,v;    for(t=box[now];t+1;t=e[t].next)    {        v=e[t].to;        if(v!=fa[now]&&v!=son[now])        dfs2(v,v);    }}

 

The above is the splitting process. For information on how to maintain the path between two points after the tree link is divided, see the implementation of tree link separation in the LCA.

It should be noted that for some questions, the weights to be modified or the requested weights are on the points and some are on the edge, although the process has not changed during partitioning, however, there are differences in processing. The specific differences are shown in the following two questions.

 

The condition of the weight value on the edge.

Http://codeforces.com/problemset/problem/165/D

Codeforces 165d beard Graph

For a tree, each side of the tree has a color, black or white. At first, all sides are black and there are two operations:

Operation 1: Turn the I side into white or black.

Operation 2: Ask U and V for the shortest path only after black.

Idea: in fact, this question does not need to be separated by tree links. There are more efficient methods, but I cannot think of a better example at the moment.

Because it is a tree, the path between the two points is determined. Therefore, you only need to determine whether all the edges in the path are black edges. All the edges are black edges, which means no white edges. So we can do this, after dividing each edge into a line segment tree, we set the weight of all edges to 0 and operate on 1. If we want to change one edge to black, if the value of the Line Segment tree is 0, otherwise the value is 1. For operation 2, we only need to check whether the right of the two paths is 0. If the value is 0, the distance between two points is returned, otherwise, 0 is returned.

Code:

 

# Include <iostream> # include <string. h> # include <stdio. h> # include <algorithm> # define maxn 100010 using namespace STD; # define mid (T [p]. L + T [p]. r)> 1) # define ls (P <1) # define RS (LS | 1) struct tree {int L, R; int sum ;} T [maxn <2]; void pushup (INT p) {T [p]. sum = T [ls]. sum + T [RS]. SUM;} void build (int p, int L, int R) {T [p]. L = L, t [p]. R = r, t [p]. sum = 0; If (L = r) return; build (LS, L, mid); Build (RS, Mid + 1, R);} void add (INT P, Int X, int Val) {If (T [p]. L = T [p]. r) {T [p]. sum + = val; return;} If (x <= mid) add (LS, X, Val); else add (RS, X, Val); pushup (p );} int query (int p, int L, int R) {If (T [p]. L = L & T [p]. R = r) {return T [p]. SUM;} If (L> mid) return query (RS, L, R); else if (r <= mid) return query (LS, L, R ); else return query (LS, L, mid) + query (RS, Mid + 1, R);} int siz [maxn], top [maxn], son [maxn], dep [maxn], W [maxn], Fa [maxn]; struct edge {int to; int n EXT;} e [maxn <1]; int box [maxn], CNT, TOT; void Init () {tot = 0; son [0] = Dep [0] = 0; memset (box,-1, sizeof (box); CNT = 0;} void add (int from, int) {e [CNT]. to = to; E [CNT]. next = Box [from]; Box [from] = CNT ++;} void DFS (INT now, int pre) {siz [now] = 1; fa [now] = pre; son [now] = 0; Dep [now] = Dep [pre] + 1; int T, V; For (t = Box [now]; t + 1; t = E [T]. next) {v = E [T]. to; If (V! = Pre) {DFS (v, now); siz [now] + = siz [v]; If (siz [son [now] <siz [v]) {son [now] = V ;}}} void dfs2 (INT now, int TP) {W [now] =++ tot; top [now] = TP; if (son [now]) dfs2 (son [now], top [now]); int T, V; For (t = Box [now]; t + 1; T = E [T]. next) {v = E [T]. to; If (V! = Fa [now] & V! = Son [now]) dfs2 (V, V) ;}} int solve (int A, int B) {int F1 = top [a], f2 = top [B], dist = 0; while (F1! = F2) {If (DEP [F1] <Dep [F2]) {swap (F1, F2); swap (a, B );} dist + = W [a]-W [F1] + 1; int TMP = query (1, W [F1], W [a]); If (TMP) return-1; A = Fa [F1]; F1 = top [a];} if (a = B) Return Dist; // note here else {If (DEP [a]> Dep [B]) Swap (a, B); int TMP = query (1, W [son [a], W [B]); // note that if (TMP) Return-1; return Dist + W [B]-W [a];} int edge [maxn] [2]; int main () {int N, Q, I, a, B; scanf ("% d", & N); Init (); for (I = 1; I <n; I ++) {scanf ("% d", & edge [I] [0], & edge [I] [1]); add (edge [I] [0], edge [I] [1]); add (edge [I] [1], edge [I] [0]);} build (1, 1, n); DFS (1, 0); dfs2 (1, 1); scanf ("% d", & Q ); while (Q --) {int K; scanf ("% d", & K); If (k = 3) {scanf ("% d", &, & B); printf ("% d \ n", solve (A, B);} else {scanf ("% d", & I); int TMP; if (DEP [edge [I] [0]> Dep [edge [I] [1]) TMP = edge [I] [0]; else TMP = edge [I] [1]; If (k = 1) Add (1, W [TMP],-1); else add (1, W [TMP], 1) ;}} return 0 ;}

Where the weights are located:

 

Http://acm.hdu.edu.cn/showproblem.php? PID = 1, 3966

HDU: 3966 Aragorn's story

Question: Give a tree the weight of all vertices in the path between two points, increase or decrease the number, and ask about the current weight of a specific point.

Train of Thought: The idea should be clear. After dividing the tree, put it into the line segment tree for maintenance.

The Code is as follows:

 

# Pragma comment (linker, "/Stack: interval, interval 0000") # include <iostream> # include <string. h> # include <stdio. h> # include <algorithm> # define maxn 50010 using namespace STD; # define mid (T [p]. L + T [p]. r)> 1) # define ls (P <1) # define RS (LS | 1) struct tree {int L, R; int lazy ;} T [maxn <2]; int siz [maxn], top [maxn], son [maxn], DEP [maxn], W [maxn], Fa [maxn], num [maxn], TT [maxn]; void Pushdown (INT p) {If (T [p]. lazy) {T [ls]. la ZY + = T [p]. lazy; t [RS]. lazy + = T [p]. lazy; t [p]. lazy = 0 ;}} void build (int p, int L, int R) {T [p]. L = L, t [p]. R = r, t [p]. lazy = 0; If (L = r) {T [p]. lazy = num [TT [l]; return;} build (LS, L, mid); Build (RS, Mid + 1, R);} void add (int p, int L, int R, int Val) {If (T [p]. L = L & T [p]. R = r) {T [p]. lazy + = val; return;} Pushdown (p); If (r <= mid) add (LS, L, R, Val); else if (L> mid) add (RS, L, R, Val); else {Add (LS, L, mid, Val); add (RS, Mid + 1, R, VA L) ;}} int query (int p, int X) {If (T [p]. L = T [p]. r) {return T [p]. lazy;} Pushdown (p); If (x> mid) return query (RS, x); else return query (LS, x);} struct edge {int; int next;} e [maxn <1]; int box [maxn], CNT, TOT; void Init () {tot = 0; son [0] = Dep [0] = 0; memset (box,-1, sizeof (box); CNT = 0;} void add (int from, int) {e [CNT]. to = to; E [CNT]. next = Box [from]; Box [from] = CNT ++;} void DFS (INT now, int pre) {siz [now] = 1; Fa [No W] = pre; son [now] = 0; Dep [now] = Dep [pre] + 1; int T, V; For (t = Box [now]; t + 1; t = E [T]. next) {v = E [T]. to; If (V! = Pre) {DFS (v, now); siz [now] + = siz [v]; If (siz [son [now] <siz [v]) {son [now] = V ;}}} void dfs2 (INT now, int TP) {W [now] =++ tot; TT [tot] = now; top [now] = TP; If (son [now]) dfs2 (son [now], top [now]); int T, V; for (t = Box [now]; t + 1; t = E [T]. next) {v = E [T]. to; If (V! = Fa [now] & V! = Son [now]) dfs2 (V, V) ;}} void solve (int A, int B, int Val) {int F1 = top [a], f2 = top [B]; while (F1! = F2) {If (DEP [F1] <Dep [F2]) {swap (F1, F2); swap (a, B);} Add (1, W [F1], W [a], Val); A = Fa [F1]; F1 = top [a];} if (a = B) {Add (1, W [a], W [a], Val); // note here} else {If (DEP [a]> Dep [B]) Swap (A, B ); add (1, W [a], W [B], Val); // note here} int main () {freopen ("dd.txt", "r ", stdin); int n, m, Q, A, B, C; char STR [2]; while (scanf ("% d", & N, & M, & Q )! = EOF) {Init (); int I; for (I = 1; I <= N; I ++) {scanf ("% d ", & num [I]) ;}for (I = 1; I <= m; I ++) {scanf ("% d", & A, & B ); add (a, B); add (B, A);} DFS (); dfs2 (); Build (, n); While (Q --) {int node; scanf ("% s", STR); If (STR [0] = 'q') {scanf ("% d", & node ); printf ("% d \ n", query (1, W [node]);} else {scanf ("% d", & A, & B, & C); If (STR [0] = 'I') solve (A, B, C); else solve (a, B,-C );}}} return 0 ;}

I have marked out the notes in the code,

 

The difference is that when we modify the last chain, that is, when A and B are in the same heavy chain, we may set Dep [a] <= Dep [B], at this time, we know that a is the two-point LCA of V and W. Because when we place the heavy chain into the line segment tree during tree segmentation, in fact, the points and edges correspond One to One. Each point corresponds to the edge of its parent node and connected to it. For the root node, you can set a virtual node as the parent node of the root node. In this way, the operations in the line segment tree can remain unchanged (in fact, for convenience ). If the weight value is on the edge, the edge corresponding to the LCA is not in the path between V and W. Therefore, we need to update one less edge. If the weight value is at a specific point, the LCA is obviously in the path between V and W, and the LCA needs to be updated. This is the difference between the two questions.

PS: in fact, there are still a lot of applications for tree link splitting, but this weak dish has not been learned yet. Here we just summarize the most basic applications. I hope you will not be able to use BS.

PS2: DFS writing method is easy to crack the stack, so there are non-recursive writing methods, such as BFS Writing Method and simulation stack, but I haven't studied it yet...

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.