樹鏈剖分

樹鏈剖分,電腦程式術語,一顆樹,將其劃分為若干條鏈,每個點屬於且僅屬於一條鏈。

基本定義

樹鏈剖分:
目標: 一顆樹,,將其劃分為若干條鏈,,,每個點屬於且僅屬於一條鏈,,,
劃分的方法很多,,,不過按某種方法劃分卻有一條很好的性質,,,任意一點到根結點的路徑上的鏈的個數不超過logn條,,,
於是如果能把問題轉化為維護點到根結點路徑的問題,,再用線性結構維護每條鏈,,那么複雜度就是 維護鏈的複雜度*logn,,很優秀了,,,

方法

常見的路徑剖分的方法是輕重邊剖分,即把樹中的邊分為輕重兩部分,方法:設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&#91;i&#93;)以及點i的深度(設為DEP&#91;i&#93;);
<2>自底向上計算每個點的SZ值,同時劃分輕重邊(對於有根樹中的每條邊設立Z域,Z=1為重邊,Z=0為輕邊);
<3>求出重鏈,建立線段樹:
求重鏈的方法:由於重鏈只會在葉結點處結束,因此從每個葉結點開始上溯,直到上溯到根或者遇到輕邊為止。為了方便,需要對每個結點i記錄以下4個值:UP&#91;i&#93;表示點i所在重鏈最頂上的那個結點的編號;ord&#91;i&#93;表示點i是其所在重鏈的上起第幾個點(從0開始);tot&#91;i&#93;表示點i所在重鏈上有幾個結點;root&#91;i&#93;表示點i所在重鏈建成的線段樹的編號(這樣經常寫的opr(0, n-1, root)就可以表示成opr(0, tot&#91;i&#93;-1, root&#91;i&#93;))。求出重鏈以後,對沿途經歷的所有邊的權值倒序寫入數組W0,再對數組W0建線段樹即可。考慮到不同線段樹的大小可能不同,這裡採用壓縮處理,這樣就需要記錄每個點的LCHRCH編號(見代碼);
<4>對樹進行DFS遍歷,求出序列A;
<5>求出序列A的倍增最小值,存放在A0&#91;&#93;&#91;&#93;里(注意:A和A0中記載的都是編號,而判定大小的關鍵字是深度);
<6>求出LOG&#91;i&#93;=log2i(下取整);
(3)執行部分:對於詢問操作,求LCA,不斷上溯,對於重鏈線上段樹里找即可,注意線段樹的左右端點,尤其是當UP在LCA之上時,只能上溯到LCA處。
劃分方法:
先預處理出以每個節點為根的子樹所包含的節點的總數,即size,,
然後 每個節點和其size最大的出邊(不含父親方向) 連邊(紅色邊),,,
這樣由紅色邊連在一起的就是一條鏈,,,,然後剩餘的單個的點也看作一條鏈,,,,
圖中共有5條鏈,,,
1-6-8-10,,,,2-3-4,,,5,,,,7,,,,9
這樣我們便達到了目的,,每個點僅屬於一條鏈,,,
編程辦法,,,怎么簡單怎么來
模擬遞歸棧,,,
計算機的速度越來越高,,,使題目的數據越來越大,,這樣用遞歸的辦法就越來越容易暴棧,,
一個10萬的點的鏈進行遞歸深搜,,,就幾乎一定會暴棧,,,
模擬棧的方法:
參數,,,,如果只有一個參數,,用一個數組既可,,,有多個參數就用結構體存儲每個參數,,
自底向上的更新,,,如果沒有這種更新,,,直接寫既可,,如果有的話,,,每個節點被取出2次,,,第一次不彈棧,,第二次彈掉,,,第一次將子節點入棧,,並處理自頂向下的更新,,,第二次處理自底向上的更新,,,,

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&#91;MAXN << 2&#93;, E&#91;MAXN + MAXN + 1&#93;;
struct node {
int maxv, lch, rch;
} T&#91;MAXN << 2&#93;;
int n, m0, m, N, _a&#91;MAXN&#93;, _b&#91;MAXN&#93;, FA&#91;MAXN&#93;, Q&#91;MAXN&#93;, SZ&#91;MAXN&#93;, DEP&#91;MAXN&#93;, W0&#91;MAXN&#93;, UP&#91;MAXN&#93;, ord&#91;MAXN&#93;, root&#91;MAXN&#93;, tot&#91;MAXN&#93;;
int n1, stk&#91;MAXN&#93;, st&#91;MAXN&#93;, A&#91;MAXN << 2&#93;, A0&#91;MAXN << 2&#93;&#91;MAXS&#93;, FF&#91;MAXN&#93;, LOG&#91;MAXN << 2&#93;, l0, r0, x0, res;
bool vst&#91;MAXN&#93;;
void init_d()
{
re(i, n) E0&#91;i&#93;.pre = E0&#91;i&#93;.next = E&#91;i&#93;.pre = E&#91;i&#93;.next = i;
m0 = m = n;
}
void add_edge0(int a, int b, int w)
{
E0&#91;m0&#93;.a = a; E0&#91;m0&#93;.b = b; E0&#91;m0&#93;.w = w; E0&#91;m0&#93;.pre = E0&#91;a&#93;.pre; E0&#91;m0&#93;.next = a; E0&#91;a&#93;.pre = m0; E0&#91;E0&#91;m0&#93;.pre&#93;.next = m0++;
E0&#91;m0&#93;.a = b; E0&#91;m0&#93;.b = a; E0&#91;m0&#93;.w = w; E0&#91;m0&#93;.pre = E0&#91;b&#93;.pre; E0&#91;m0&#93;.next = b; E0&#91;b&#93;.pre = m0; E0&#91;E0&#91;m0&#93;.pre&#93;.next = m0++;
}
void add_edge(int a, int b, int w)
{
E&#91;m&#93;.a = a; E&#91;m&#93;.b = b; E&#91;m&#93;.w = w; E&#91;m&#93;.Z = 0; E&#91;m&#93;.pre = E&#91;a&#93;.pre; E&#91;m&#93;.next = a; E&#91;a&#93;.pre = m; E&#91;E&#91;m&#93;.pre&#93;.next = m++;
}
int mkt(int l, int r)
{
int No = ++N;
if (l == r) {
T&#91;No&#93;.maxv = W0&#91;l&#93;; T&#91;No&#93;.lch = T&#91;No&#93;.rch = 0;
} else {
int mid = l + r >> 1, l_r = mkt(l, mid), r_r = mkt(mid + 1, r); T&#91;No&#93;.lch = l_r; T&#91;No&#93;.rch = r_r;
if (T&#91;l_r&#93;.maxv >= T&#91;r_r&#93;.maxv) T&#91;No&#93;.maxv = T&#91;l_r&#93;.maxv; else T&#91;No&#93;.maxv = T&#91;r_r&#93;.maxv;
}
return No;
}
void prepare()
{
re(i, n) vst&#91;i&#93; = 0; Q&#91;0&#93; = 0; vst&#91;0&#93; = 1; DEP&#91;0&#93; = 0; FA&#91;0&#93; = -1;
int i0, j0;
for (int front=0, rear=0; front<=rear; front++) {
i0 = Q&#91;front&#93;;
for (int p=E0&#91;i0&#93;.next; p != i0; p=E0&#91;p&#93;.next) {
j0 = E0&#91;p&#93;.b;
if (!vst&#91;j0&#93;) {add_edge(i0, j0, E0&#91;p&#93;.w); vst&#91;j0&#93; = 1; Q&#91;++rear&#93; = j0; FA&#91;j0&#93; = m - 1; DEP&#91;j0&#93; = DEP&#91;i0&#93; + 1;}
}
}
int maxSZ, x, n0, root0;
rre(i, n) {
i0 = Q&#91;i&#93;; SZ&#91;i0&#93; = 1; maxSZ = 0;
for (int p=E&#91;i0&#93;.next; p != i0; p=E&#91;p&#93;.next) {SZ&#91;i0&#93; += SZ&#91;j0 = E&#91;p&#93;.b&#93;; if (SZ&#91;j0&#93; > maxSZ) {maxSZ = SZ&#91;j0&#93;; x = p;}}
if (SZ&#91;i0&#93; > 1) E&#91;x&#93;.Z = 1; //Attention
}
UP&#91;0&#93; = 0; ord&#91;0&#93; = 0; N = 0;
re2(i, 1, n) {
i0 = Q&#91;i&#93;; x = FA&#91;i0&#93;; if (E&#91;x&#93;.Z) {UP&#91;i0&#93; = UP&#91;E&#91;x&#93;.a&#93;; ord&#91;i0&#93; = ord&#91;E&#91;x&#93;.a&#93; + 1;} else {UP&#91;i0&#93; = i0; ord&#91;i0&#93; = 0;}
if (SZ&#91;i0&#93; == 1 && ord&#91;i0&#93;) {
j0 = UP&#91;i0&#93;; n0 = ord&#91;i0&#93;;
for (int j=i0; j!=j0; j=E&#91;FA&#91;j&#93;&#93;.a) {tot&#91;j&#93; = ord&#91;i0&#93;; W0&#91;--n0&#93; = E&#91;FA&#91;j&#93;&#93;.w;} tot&#91;j0&#93; = ord&#91;i0&#93;; //Attention
root0 = mkt(0, ord&#91;i0&#93; - 1); //Attention
for (int j=i0; j!=j0; j=E&#91;FA&#91;j&#93;&#93;.a) root&#91;j&#93; = root0; root&#91;j0&#93; = root0; //Attention
}
}
re(i, n) {st&#91;i&#93; = E&#91;i&#93;.next; FF&#91;i&#93; = -1;} stk&#91;0&#93; = 0; int tp = 0; n1 = 1; A&#91;0&#93; = FF&#91;0&#93; = 0;
while (tp >= 0) {
i0 = stk&#91;tp&#93;; x = st&#91;i0&#93;;
if (x != i0) {
j0 = E&#91;x&#93;.b; if (FF&#91;j0&#93; == -1) FF&#91;j0&#93; = n1; A&#91;n1++&#93; = j0; st&#91;i0&#93; = E&#91;x&#93;.next; stk&#91;++tp&#93; = j0;
} else {
if (tp) A&#91;n1++&#93; = stk&#91;tp - 1&#93;;
tp--;
}
}
rre(i, n1) {
A0&#91;i&#93;&#91;0&#93; = A&#91;i&#93;; x = 1;
re2(j, 1, MAXS) if (i + (x << 1) <= n1) {
if (DEP&#91;A0&#91;i&#93;&#91;j - 1&#93;&#93; <= DEP&#91;A0&#91;i + x&#93;&#91;j - 1&#93;&#93;) A0&#91;i&#93;&#91;j&#93; = A0&#91;i&#93;&#91;j - 1&#93;; else A0&#91;i&#93;&#91;j&#93; = A0&#91;i + x&#93;&#91;j - 1&#93;; //Attention
x <<= 1;
} else break;
}
int _x; x = 1;
re(i, MAXS) {
_x = x << 1;
if (_x < n1) re2(j, x, _x) LOG&#91;j&#93; = i; else {re2(j, x, n1) LOG&#91;j&#93; = i; break;}
x = _x;
}
}
void opr0(int l, int r, int No)
{
if (l == l0 && r == l0) T&#91;No&#93;.maxv = x0; else {
int mid = l + r >> 1, lch = T&#91;No&#93;.lch, rch = T&#91;No&#93;.rch;
if (mid >= l0) opr0(l, mid, lch);
if (mid < l0) opr0(mid + 1, r, rch);
if (T&#91;lch&#93;.maxv >= T&#91;rch&#93;.maxv) T&#91;No&#93;.maxv = T&#91;lch&#93;.maxv; else T&#91;No&#93;.maxv = T&#91;rch&#93;.maxv;
}
}
void opr1(int l, int r, int No)
{
if (l >= l0 && r <= r0) {
if (T&#91;No&#93;.maxv > res) res = T&#91;No&#93;.maxv;
} else {
int mid = l + r >> 1;
if (mid >= l0) opr1(l, mid, T&#91;No&#93;.lch);
if (mid < r0) opr1(mid + 1, r, T&#91;No&#93;.rch);
}
}
int main()
{
int tests, a0, b0, w0, LCA, LOG0, FF0, FF1, p0, tmp;
char ss&#91;20&#93;, 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&#91;i&#93; = a0; _b&#91;i&#93; = 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 = 0; else res = -INF;
FF0 = FF&#91;a0&#93;; FF1 = FF&#91;b0&#93;;
if (FF0 > FF1) {tmp = FF0; FF0 = FF1; FF1 = tmp;}
LOG0 = LOG&#91;FF1 - FF0 + 1&#93;;
if (DEP&#91;A0&#91;FF0&#93;&#91;LOG0&#93;&#93; <= DEP&#91;A0&#91;FF1 - (1 << LOG0) + 1&#93;&#91;LOG0&#93;&#93;) LCA = A0&#91;FF0&#93;&#91;LOG0&#93;; else LCA = A0&#91;FF1 - (1 << LOG0) + 1&#93;&#91;LOG0&#93;;
while (a0 != LCA) {
p0 = FA&#91;a0&#93;;
if (E&#91;p0&#93;.Z) {
r0 = ord&#91;a0&#93; - 1; if (DEP&#91;UP&#91;a0&#93;&#93; >= DEP&#91;LCA&#93;) l0 = 0; else l0 = ord&#91;LCA&#93;; //Attention
if (l0 <= r0) opr1(0, tot&#91;a0&#93; - 1, root&#91;a0&#93;); //Attention
if (l0) a0 = LCA; else a0 = UP&#91;a0&#93;;
} else {
if (E&#91;p0&#93;.w > res) res = E&#91;p0&#93;.w;
a0 = E&#91;p0&#93;.a;
}
}
while (b0 != LCA) {
p0 = FA&#91;b0&#93;;
if (E&#91;p0&#93;.Z) {
r0 = ord&#91;b0&#93; - 1; if (DEP&#91;UP&#91;b0&#93;&#93; >= DEP&#91;LCA&#93;) l0 = 0; else l0 = ord&#91;LCA&#93;; //Attention
if (l0 <= r0) opr1(0, tot&#91;b0&#93; - 1, root&#91;b0&#93;); //Attention
if (l0) b0 = LCA; else b0 = UP&#91;b0&#93;;
} else {
if (E&#91;p0&#93;.w > res) res = E&#91;p0&#93;.w;
b0 = E&#91;p0&#93;.a;
}
}
printf("%d\n", res);
} else if (!strcmp(ss, "CHANGE")) {
scanf("%d%d", &a0, &w0); b0 = _b&#91;--a0&#93;; a0 = _a&#91;a0&#93;;
if (FA&#91;b0&#93; == -1 || E&#91;FA&#91;b0&#93;&#93;.a != a0) {tmp = a0; a0 = b0; b0 = tmp;}
p0 = FA&#91;b0&#93;;
if (E&#91;p0&#93;.Z) {
l0 = ord&#91;a0&#93;; x0 = w0; opr0(0, tot&#91;a0&#93; - 1, root&#91;a0&#93;);
} else E&#91;p0&#93;.w = w0;
} else break;
}
}
return 0;
}
樹的路徑剖分是解決樹上路徑的操作查詢問題的有力工具,它還有一些更為強大的套用,以後再來搞……

經典題目

// 題意: 給定一顆帶權樹,要求實現2個操作: 1、修改邊權, 2、得到2個點的路徑上的最大邊權值;
// 樹鏈剖分, 經典寫法, 把輕邊也加入線段樹;
// 卡時間的題,用read(), readchar(),讀入可以加快至少1s,又多會了點東西;
// 非常精妙的dfs處理, 非常優美的遍歷樹鏈寫法!
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
usingnamespacestd;
#define mpair make_pair
#define pii pair<int,int>
#define MM(a,b) memset(a,b,sizeof(a));
typedeflonglonglld;
typedefunsignedlonglongu64;
template<classT>boolup_max(T&a,constT& b){return b>a?a=b,1:0;}
template<classT>boolup_min(T&a,constT& b){return b<a?a=b,1:0;}
#define maxn 10020
#define inf -1000000000
int n,top;
structEdge{
intv,id;
Edge*next;
}*adj&#91;maxn&#93;,edge&#91;maxn+maxn&#93;;
voidAddedge(Intu,intv,intid){
Edge*p=&edge&#91;++top&#93;;
p->v=v, p->id=id;
p->next=adj&#91;u&#93;;
adj&#91;u&#93;= p;
}
intw&#91;maxn&#93;; // the weight of the edge i;
intfather&#91;maxn&#93;,d&#91;maxn&#93;; // the father node and the depth;
intLink&#91;maxn&#93;; // mark the node which edge is;
intson&#91;maxn&#93;; // mark which node is the next node, that them form a weighted edge;
intsonedge&#91;maxn&#93;; // mark the id of the edge the node that from father;
intdfs(intu,intFa,intdep,intid){
d&#91;u&#93;=dep,father&#91;u&#93;=Fa,Link&#91;u&#93;=id;
intsize=1,maxsize=inf;
for(Edge*p=adj&#91;u&#93;;p;p= p->next){
intv= p->v;
if( v==Fa ) continue;
inttmp=dfs(v,u,dep+1,p->id);
size+=tmp;
if( up_max( maxsize,tmp ) ){
son&#91;u&#93;=v,sonedge&#91;u&#93;= p->id;
}
}
returnsize;
}
int N,T&#91;3*maxn&#93;;
intpre&#91;maxn&#93;; // the root of the weighted edge;
intmark&#91;maxn&#93;; // mark the position of the edge i on segment tree;
intoppomark&#91;maxn&#93;; // opposite to mark&#91;&#93; , convenient for us to build segment tree;
voiddfs2(intu,intf){
pre&#91;u&#93;=f,mark&#91;Link&#91;u&#93;&#93;=++N;
oppomark&#91;N&#93;=Link&#91;u&#93;;
if( !son&#91;u&#93; ) return;
dfs2( son&#91;u&#93;,f ); ////////////
for(Edge*p=adj&#91;u&#93;;p;p= p->next){
intv= p->v;
if( v==father&#91;u&#93; || son&#91;u&#93;==v ) continue; ///////////
dfs2(v,v);
}
}
voidcreate(intu,intl,intr){
if(l==r){T&#91;u&#93;=w&#91;oppomark&#91;l&#93;&#93;;return; }
intmid= (l+r)>>1;
create( u+u,l,mid );
create( u+u+1,mid+1,r );
T&#91;u&#93;=max( T&#91;u+u&#93;,T&#91;u+u+1&#93; );
}
voidupdate(intu,intl,intr,intpos,intval){
if(l==r){
T&#91;u&#93;=val; return;
}
intmid= (l+r)>>1;
if(pos<=mid) update(u+u,l,mid,pos,val);
elseupdate(u+u+1,mid+1,r,pos,val);
T&#91;u&#93;=max( T&#91;u+u&#93;,T&#91;u+u+1&#93; );
}
intget_max(intu,intl,intr,inttl,inttr){
if(tl<=l&&r<=tr) returnT&#91;u&#93;;
intmid= (l+r)>>1;
if( tr<=mid ) returnget_max(u+u,l,mid,tl,tr);
elseif(tl>mid) returnget_max(u+u+1,mid+1,r,tl,tr);
else{
intt1=get_max(u+u,l,mid,tl,mid);
intt2=get_max(u+u+1,mid+1,r,mid+1,tr);
returnmax( t1,t2 );
}
}
voidbuild_tree(){
w&#91;0&#93;=inf;
N=0;
for(inti=1;i<=n;++i) son&#91;i&#93;=0;
dfs(1,-1,1,0);
dfs2(1,1);
create(1,1,N);
}
/*************************** 非常好的寫法 ****************************/
intread(){
charc;
while( c=getchar(),!isdigit(c) );
intr=c-'0';
while( c=getchar(),isdigit(c) ) r=r*10+c-'0';
returnr;
}
charreadchar(){
charc,r;
while( r=getchar(),!isalpha(r) );
while( c=getchar(),isalpha(c) );
returnr;
}
/*********************************************************************/
voidsolve(){
charch;
intx,y;
while( ch=readchar() ,'D'!=ch){
x=read(),y=read();
if( 'C'==ch){
update( 1,1, N,mark&#91;x&#93;,y );
}
else{
if( x==y){
puts("0"); continue;
}
intans=inf,nx,ny;
for( ; pre&#91;x&#93;!=pre&#91;y&#93; ; x=father&#91;nx&#93;){
nx=pre&#91;x&#93;,ny=pre&#91;y&#93;;
//if( d&#91;x&#93;<d&#91;y&#93; ) swap(x,y), swap(nx,ny); /////////swap(nx,ny); we should not write like this!!!!
if( d&#91;nx&#93;<d&#91;ny&#93; ) swap(x,y),swap(nx,ny); //////// we choose the root to compare to determine move which node up;
up_max( ans,get_max( 1,1, N,mark&#91;Link&#91;nx&#93;&#93;,mark&#91;Link&#91;x&#93;&#93; ) );
}
if( d&#91;x&#93;<d&#91;y&#93; ) swap(x,y);
if( x!=y )
up_max( ans,get_max( 1,1, N,mark&#91;sonedge&#91;y&#93;&#93;,mark&#91;Link&#91;x&#93;&#93; ) );
printf("%d\n",ans);
}
}
}
intmain()
{
intCas,i,u,v;
Cas=read();
while(Cas--){
n=read();
top=0;
MM( adj,0 );
for(i=1;i<n;++i){
u=read(),v=read(),w&#91;i&#93;=read();
Addedge(u,v,i);
Addedge(v,u,i);
}
build_tree();
solve();
}
}

CPP標程

<PRE class=cpp name="code">/*</PRE>
<PRE class=cpp name="code">節點的標號從1開始
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10000; //N為節點的個數
struct e{
int v;
e* NXT;
}es&#91;N<<1&#93;, *fir&#91;N&#93;;
struct node{
int ls, rs; //左右兒子的下標,為-1表示空
int l, r; //區間的左右標號
//數據域,根據不同的需要添加,數據的down和update和線段樹的無異
int mid() { return (l + r) >> 1; }
}nodes&#91;N<<1&#93;;
int n, en;
int que&#91;N&#93;, par&#91;N&#93;, dep&#91;N&#93;, root&#91;N&#93;, seg&#91;N&#93;, st&#91;N&#93;, ed&#91;N&#93;, top&#91;N&#93;, sons&#91;N&#93;, id&#91;N&#93;;
//que用於BFS,par記錄父節點,dep記錄節點的深度。 root&#91;i&#93;為鏈i的根節點,seg用於在鏈上建線段樹,
//st&#91;i&#93;,ed&#91;i&#93;分別為鏈i的左右端點,top&#91;i&#93;為鏈i的頂部的節點,sons&#91;i&#93;為節點i的兒子節點
//id&#91;i&#93;是節點i所屬的鏈的標號,
int ln, CNT, tr; //ln是鏈的個數,cnt為節點的個數,tr是樹的根節點
inline void add_e(int u, int v){
es&#91;en&#93;.v = v;
es&#91;en&#93;.nxt = fir&#91;u&#93;;
fir&#91;u&#93; = &es&#91;en++&#93;;
}
inline void newNode(int& id, int l, int r){
nodes&#91;cnt&#93;.ls = nodes&#91;cnt&#93;.rs = -1;
nodes&#91;cnt&#93;.l = l;
nodes&#91;cnt&#93;.r = r;
id = cnt++;
}
void build(int& id, int l, int r){ //在剖分出來的鏈上構建線段樹
newNode(id, l, r);
if(l >= r){
//seg&#91;l&#93;為落在這個線段樹節點上的原樹中的節點
return ;
}
int mid = (l+r)>>1;
build(nodes&#91;id&#93;.ls, l, mid);
build(nodes&#91;id&#93;.rs, mid+1, r);
}
void initTree(){ //初始化剖分樹
//確定父親
int l, r, u, v, i;
e* cur;
l = r = 0;
que&#91;r++&#93; = tr;
par&#91;tr&#93; = -1;
dep&#91;tr&#93; = 0;
while(l != r){
u = que&#91;l++&#93;;
int g = 1;
for(cur = fir&#91;u&#93;; cur; cur = cur->nxt){
if((v = cur->v) != par&#91;u&#93;){
que&#91;r++&#93; = v;
par&#91;v&#93; = u;
dep&#91;v&#93; = dep&#91;u&#93;+1;
}
}
}
//計運算元樹大小
for(i = 1; i <= n; i++){
sons&#91;i&#93; = 1;
id&#91;i&#93; = -1;
}
for(i = r-1; i >= 0; i--){
u = que&#91;i&#93;;
if(par&#91;u&#93; >= 0){
sons&#91;par&#91;u&#93;&#93; += sons&#91;u&#93;;
}
}
//剖分鏈
l = r = 0;
que&#91;r++&#93; = tr;
ln = cnt = 0;
while(l != r){
u = que&#91;l++&#93;;
st&#91;ln&#93; = dep&#91;u&#93;; //用節點的深度作為線段樹中區間的左右標號
top&#91;ln&#93; = u;
while(u >= 0){
id&#91;u&#93; = ln;
ed&#91;ln&#93; = dep&#91;u&#93;;
seg&#91;dep&#91;u&#93;&#93; = u;
int best;
for(cur = fir&#91;u&#93;, best=-1; cur; cur = cur->nxt){
if(id&#91;v = cur->v&#93; == -1){
if(best == -1 || (best >= 0 && sons&#91;v&#93; > sons&#91;best&#93;)){
best = v;
}
}
}
if(best >= 0){
for(cur = fir&#91;u&#93;; cur; cur = cur->nxt){
if(id&#91;v = cur->v&#93; == -1 && best != v){
que&#91;r++&#93; = v;
}
}
}
u = best;
}
root&#91;ln&#93; = -1;
build(root&#91;ln&#93;, st&#91;ln&#93;, ed&#91;ln&#93;);
ln++;
}
}
void lqry(int& id, int ql, int qr){
if(id == -1) return ;
if(ql <= nodes&#91;id&#93;.l && nodes&#91;id&#93;.r <= qr){
return ;
}
if(nodes&#91;id&#93;.l == nodes&#91;id&#93;.r){
return ;
}
int mid = (nodes&#91;id&#93;.l+nodes&#91;id&#93;.r)>>1;
if(ql <= mid){
lqry(nodes&#91;id&#93;.ls, ql, qr);
}
if(qr > mid){
lqry(nodes&#91;id&#93;.rs, ql, qr);
}
}
void qry(int u, int v){ //查詢u和v之間的最大值
while(id&#91;u&#93; != id&#91;v&#93;){
if(id&#91;u&#93; > id&#91;v&#93;){
swap(u, v);
}
int b = id&#91;v&#93;;
lqry(root&#91;b&#93;, st&#91;b&#93;, dep&#91;v&#93;);
v = par&#91;top&#91;b&#93;&#93;;
}
if(dep&#91;u&#93; > dep&#91;v&#93;){
swap(u, v);
}
lqry(root&#91;id&#91;u&#93;&#93;, dep&#91;u&#93;, dep&#91;v&#93;);
}
int main(){
return 0;
}
</PRE>
&nbsp;

相關詞條

相關搜尋

熱門詞條

聯絡我們