• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            原題地址
            【有關樹的路徑剖分的東東在網上介紹的太多了……】
            常見的路徑剖分的方法是輕重邊剖分,即把樹中的邊分為輕重兩部分,方法:設SZ[i]為以i為根的子樹的大小(結點總數),則若點x不是葉結點,則其子結點中SZ值最大的(注意,有多個SZ值最大的子結點應任選一個,只能選一個,防止出現重鏈相交,引發歧義)點y,邊(x, y)稱為重邊,其余的邊都是輕邊。首尾相連的重邊稱為重鏈(注意一定是自上而下的),則一個很顯然的性質是:從根結點到任意點i路徑上的輕邊與重鏈的總數都不會超過O(log2N)。然后,對每條重鏈上的邊建立線段樹,每當遇到改值操作,若是輕邊就直接改,若是重邊就在線段樹里改;遇到找x、y路徑上邊權最大值的操作,只要找到LCA(x, y),然后從x、y開始沿著樹邊上溯到LCA(x, y)處,對于中間的每條輕邊和重鏈(線段樹內)導出最大值即可。

            求LCA:可以對整棵樹作深度優先遍歷,記下每個遇到的點(包括向上的和向下的)的編號,形成一個長度為2(N-1)的序列A,然后,找到點x、y在A中的第一次出現的位置(設為FF[x]和FF[y]),則A[FF[x]..FF[y]]中的深度最小的點的編號就是LCA(x, y),顯然這需要RMQ。

            具體步驟:
            (1)輸入部分:建立無根樹;
            (2)預處理部分:分為6步:
            <1>利用BFS將無根樹轉化為有根樹,同時求出有根樹中點i(根結點除外)到其父結點的邊的編號(設為FA[i])以及點i的深度(設為DEP[i]);
            <2>自底向上計算每個點的SZ值,同時劃分輕重邊(對于有根樹中的每條邊設立Z域,Z=1為重邊,Z=0為輕邊);
            <3>求出重鏈,建立線段樹:
            求重鏈的方法:由于重鏈只會在葉結點處結束,因此從每個葉結點開始上溯,直到上溯到根或者遇到輕邊為止。為了方便,需要對每個結點i記錄以下4個值:UP[i]表示點i所在重鏈最頂上的那個結點的編號;ord[i]表示點i是其所在重鏈的上起第幾個點(從0開始);tot[i]表示點i所在重鏈上有幾個結點;root[i]表示點i所在重鏈建成的線段樹的編號(這樣經常寫的opr(0, n-1, root)就可以表示成opr(0, tot[i]-1, root[i]))。求出重鏈以后,對沿途經歷的所有邊的權值倒序寫入數組W0,再對數組W0建線段樹即可。考慮到不同線段樹的大小可能不同,這里采用壓縮處理,這樣就需要記錄每個點的lch、rch編號(見代碼);
            <4>對樹進行DFS遍歷,求出序列A;
            <5>求出序列A的倍增最小值,存放在A0[][]里(注意:A和A0中記載的都是編號,而判定大小的關鍵字是深度);
            <6>求出LOG[i]=log2i(下取整);
            (3)執行部分:對于詢問操作,求LCA,不斷上溯,對于重鏈在線段樹里找即可,注意線段樹的左右端點,尤其是當UP在LCA之上時,只能上溯到LCA處。

            編程注意事項:建代碼中標Attention的地方。
            代碼(我這個代碼常數巨大,時間達3.61s,不知是腫么搞得,神犇來看一下啊囧):
            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>
            using namespace std;
            #define re(i, n) for (int i=0; i<n; i++)
            #define re1(i, n) for (int i=1; i<=n; i++)
            #define re2(i, l, r) for (int i=l; i<r; i++)
            #define re3(i, l, r) for (int i=l; i<=r; i++)
            #define rre(i, n) for (int i=n-1; i>=0; i--)
            #define rre1(i, n) for (int i=n; i>0; i--)
            #define rre2(i, r, l) for (int i=r-1; i>=l; i--)
            #define rre3(i, r, l) for (int i=r; i>=l; i--)
            const int MAXN = 10001, MAXS = 20, INF = ~0U >> 2;
            struct edge {
                
            int a, b, w, pre, next;
                
            bool Z;
            } E0[MAXN 
            << 2], E[MAXN + MAXN + 1];
            struct node {
                
            int maxv, lch, rch;
            } T[MAXN 
            << 2];
            int n, m0, m, N, _a[MAXN], _b[MAXN], FA[MAXN], Q[MAXN], SZ[MAXN], DEP[MAXN], W0[MAXN], UP[MAXN], ord[MAXN], root[MAXN], tot[MAXN];
            int n1, stk[MAXN], st[MAXN], A[MAXN << 2], A0[MAXN << 2][MAXS], FF[MAXN], LOG[MAXN << 2], l0, r0, x0, res;
            bool vst[MAXN];
            void init_d()
            {
                re(i, n) E0[i].pre 
            = E0[i].next = E[i].pre = E[i].next = i;
                m0 
            = m = n;
            }
            void add_edge0(int a, int b, int w)
            {
                E0[m0].a 
            = a; E0[m0].b = b; E0[m0].w = w; E0[m0].pre = E0[a].pre; E0[m0].next = a; E0[a].pre = m0; E0[E0[m0].pre].next = m0++;
                E0[m0].a 
            = b; E0[m0].b = a; E0[m0].w = w; E0[m0].pre = E0[b].pre; E0[m0].next = b; E0[b].pre = m0; E0[E0[m0].pre].next = m0++;
            }
            void add_edge(int a, int b, int w)
            {
                E[m].a 
            = a; E[m].b = b; E[m].w = w; E[m].Z = 0; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m++;
            }
            int mkt(int l, int r)
            {
                
            int No = ++N;
                
            if (l == r) {
                    T[No].maxv 
            = W0[l]; T[No].lch = T[No].rch = 0;
                } 
            else {
                    
            int mid = l + r >> 1, l_r = mkt(l, mid), r_r = mkt(mid + 1, r); T[No].lch = l_r; T[No].rch = r_r;
                    
            if (T[l_r].maxv >= T[r_r].maxv) T[No].maxv = T[l_r].maxv; else T[No].maxv = T[r_r].maxv;
                }
                
            return No;
            }
            void prepare()
            {
                re(i, n) vst[i] 
            = 0; Q[0= 0; vst[0= 1; DEP[0= 0; FA[0= -1;
                
            int i0, j0;
                
            for (int front=0, rear=0; front<=rear; front++) {
                    i0 
            = Q[front];
                    
            for (int p=E0[i0].next; p != i0; p=E0[p].next) {
                        j0 
            = E0[p].b;
                        
            if (!vst[j0]) {add_edge(i0, j0, E0[p].w); vst[j0] = 1; Q[++rear] = j0; FA[j0] = m - 1; DEP[j0] = DEP[i0] + 1;}
                    }
                }
                
            int maxSZ, x, n0, root0;
                rre(i, n) {
                    i0 
            = Q[i]; SZ[i0] = 1; maxSZ = 0;
                    
            for (int p=E[i0].next; p != i0; p=E[p].next) {SZ[i0] += SZ[j0 = E[p].b]; if (SZ[j0] > maxSZ) {maxSZ = SZ[j0]; x = p;}}
                    
            if (SZ[i0] > 1) E[x].Z = 1;   //Attention
                }
                UP[
            0= 0; ord[0= 0; N = 0;
                re2(i, 
            1, n) {
                    i0 
            = Q[i]; x = FA[i0]; if (E[x].Z) {UP[i0] = UP[E[x].a]; ord[i0] = ord[E[x].a] + 1;} else {UP[i0] = i0; ord[i0] = 0;}
                    
            if (SZ[i0] == 1 && ord[i0]) {
                        j0 
            = UP[i0]; n0 = ord[i0];
                        
            for (int j=i0; j!=j0; j=E[FA[j]].a) {tot[j] = ord[i0]; W0[--n0] = E[FA[j]].w;} tot[j0] = ord[i0];  //Attention
                        root0 = mkt(0, ord[i0] - 1);  //Attention
                        for (int j=i0; j!=j0; j=E[FA[j]].a) root[j] = root0; root[j0] = root0;  //Attention
                    }
                }
                re(i, n) {st[i] 
            = E[i].next; FF[i] = -1;} stk[0= 0int tp = 0; n1 = 1; A[0= FF[0= 0;
                
            while (tp >= 0) {
                    i0 
            = stk[tp]; x = st[i0];
                    
            if (x != i0) {
                        j0 
            = E[x].b; if (FF[j0] == -1) FF[j0] = n1; A[n1++= j0; st[i0] = E[x].next; stk[++tp] = j0;
                    } 
            else {
                        
            if (tp) A[n1++= stk[tp - 1];
                        tp
            --;
                    }
                }
                rre(i, n1) {
                    A0[i][
            0= A[i]; x = 1;
                    re2(j, 
            1, MAXS) if (i + (x << 1<= n1) {
                        
            if (DEP[A0[i][j - 1]] <= DEP[A0[i + x][j - 1]]) A0[i][j] = A0[i][j - 1]; else A0[i][j] = A0[i + x][j - 1];  //Attention
                        x <<= 1;
                    } 
            else break;
                }
                
            int _x; x = 1;
                re(i, MAXS) {
                    _x 
            = x << 1;
                    
            if (_x < n1) re2(j, x, _x) LOG[j] = i; else {re2(j, x, n1) LOG[j] = i; break;}
                    x 
            = _x;
                }
            }
            void opr0(int l, int r, int No)
            {
                
            if (l == l0 && r == l0) T[No].maxv = x0; else {
                    
            int mid = l + r >> 1, lch = T[No].lch, rch = T[No].rch;
                    
            if (mid >= l0) opr0(l, mid, lch);
                    
            if (mid < l0) opr0(mid + 1, r, rch);
                    
            if (T[lch].maxv >= T[rch].maxv) T[No].maxv = T[lch].maxv; else T[No].maxv = T[rch].maxv;
                }
            }
            void opr1(int l, int r, int No)
            {
                
            if (l >= l0 && r <= r0) {
                    
            if (T[No].maxv > res) res = T[No].maxv;
                } 
            else {
                    
            int mid = l + r >> 1;
                    
            if (mid >= l0) opr1(l, mid, T[No].lch);
                    
            if (mid < r0) opr1(mid + 1, r, T[No].rch);
                }
            }
            int main()
            {
                
            int tests, a0, b0, w0, LCA, LOG0, FF0, FF1, p0, tmp;
                
            char ss[20], ch;
                scanf(
            "%d"&tests);
                re(testno, tests) {
                    scanf(
            "%d"&n); init_d();
                    re(i, n
            -1) {scanf("%d%d%d"&a0, &b0, &w0); add_edge0(--a0, --b0, w0); _a[i] = a0; _b[i] = b0;}
                    prepare(); ch 
            = getchar();
                    
            while (1) {
                        scanf(
            "%s", ss);
                        
            if (!strcmp(ss, "QUERY")) {
                            scanf(
            "%d%d%*c"&a0, &b0); --a0; --b0;
                            
            if (a0 == b0) res = 0else res = -INF;
                            FF0 
            = FF[a0]; FF1 = FF[b0];
                            
            if (FF0 > FF1) {tmp = FF0; FF0 = FF1; FF1 = tmp;}
                            LOG0 
            = LOG[FF1 - FF0 + 1];
                            
            if (DEP[A0[FF0][LOG0]] <= DEP[A0[FF1 - (1 << LOG0) + 1][LOG0]]) LCA = A0[FF0][LOG0]; else LCA = A0[FF1 - (1 << LOG0) + 1][LOG0];
                            
            while (a0 != LCA) {
                                p0 
            = FA[a0];
                                
            if (E[p0].Z) {
                                    r0 
            = ord[a0] - 1if (DEP[UP[a0]] >= DEP[LCA]) l0 = 0else l0 = ord[LCA];  //Attention
                                    if (l0 <= r0) opr1(0, tot[a0] - 1, root[a0]);  //Attention
                                    if (l0) a0 = LCA; else a0 = UP[a0];
                                } 
            else {
                                    
            if (E[p0].w > res) res = E[p0].w;
                                    a0 
            = E[p0].a;
                                }
                            }
                            
            while (b0 != LCA) {
                                p0 
            = FA[b0];
                                
            if (E[p0].Z) {
                                    r0 
            = ord[b0] - 1if (DEP[UP[b0]] >= DEP[LCA]) l0 = 0else l0 = ord[LCA];  //Attention
                                    if (l0 <= r0) opr1(0, tot[b0] - 1, root[b0]);  //Attention
                                    if (l0) b0 = LCA; else b0 = UP[b0];
                                } 
            else {
                                    
            if (E[p0].w > res) res = E[p0].w;
                                    b0 
            = E[p0].a;
                                }
                            }
                            printf(
            "%d\n", res);
                        } 
            else if (!strcmp(ss, "CHANGE")) {
                            scanf(
            "%d%d"&a0, &w0); b0 = _b[--a0]; a0 = _a[a0];
                            
            if (FA[b0] == -1 || E[FA[b0]].a != a0) {tmp = a0; a0 = b0; b0 = tmp;}
                            p0 
            = FA[b0];
                            
            if (E[p0].Z) {
                                l0 
            = ord[a0]; x0 = w0; opr0(0, tot[a0] - 1, root[a0]);
                            } 
            else E[p0].w = w0;
                        } 
            else break;
                    }
                }
                
            return 0;
            }
            樹的路徑剖分是解決樹上路徑的操作查詢問題的有力工具,它還有一些更為強大的應用,以后再來搞……

            Feedback

            # re: QTREE——樹的路徑剖分(又稱樹鏈剖分)[未登錄]  回復  更多評論   

            2012-08-14 00:11 by 楊楊
            對于修改操作,不一定需要使用LCA
            直接貼別人的話:
            http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html
            修改操作:例如將u到v的路徑上每條邊的權值都加上某值x。
            一般人需要先求LCA,然后慢慢修改u、v到公共祖先的邊。而高手就不需要了。
            記f1 = top[u],f2 = top[v]。
            當f1 <> f2時:不妨設dep[f1] >= dep[f2],那么就更新u到f1的父邊的權值(logn),并使u = fa[f1]。
            當f1 = f2時:u與v在同一條重鏈上,若u與v不是同一點,就更新u到v路徑上的邊的權值(logn),否則修改完成;
            重復上述過程,直到修改完成

            比使用LCA快

            # re: QTREE——樹的路徑剖分(又稱樹鏈剖分)  回復  更多評論   

            2012-10-25 20:43 by Mato_No1
            @楊楊
            這個其實就是WJMZBMR自創的那種利用路徑剖分求LCA的算法,見http://www.shnenglu.com/MatoNo1/archive/2012/01/14/164163.html
            只不過這個是在求LCA的過程中順便操作了而已
            国产精品热久久无码av| 97久久国产亚洲精品超碰热| 久久精品亚洲欧美日韩久久| 内射无码专区久久亚洲| 亚洲国产精品综合久久一线| 久久久久亚洲AV片无码下载蜜桃| 久久人人爽爽爽人久久久| 国产69精品久久久久99尤物| 国产欧美久久久精品影院| 国产精品久久影院| 日本国产精品久久| 精品久久一区二区三区| 亚洲国产精品成人AV无码久久综合影院 | 精品综合久久久久久97超人| 久久99精品久久久久久齐齐| 亚洲狠狠婷婷综合久久蜜芽| 国产精品欧美久久久久天天影视 | 亚洲精品白浆高清久久久久久| 久久99国产精品二区不卡| 一个色综合久久| 久久久久久噜噜精品免费直播| 精品永久久福利一区二区| 国产精品久久婷婷六月丁香| 国产成人综合久久久久久| 高清免费久久午夜精品| 97精品国产97久久久久久免费| 久久亚洲精品无码播放| 亚洲一区二区三区日本久久九| 国产精品久久精品| 国产精品视频久久| 激情伊人五月天久久综合| 婷婷五月深深久久精品| 欧美日韩精品久久久久 | 欧美黑人激情性久久| 国内精品伊人久久久影院| 国产精品99久久久精品无码| 日本欧美国产精品第一页久久| 久久精品亚洲男人的天堂| 久久久久久国产精品美女 | 久久国产亚洲高清观看| 东方aⅴ免费观看久久av|