這貨是不是動態樹里的我就不清楚了,fhq的blog好像有提到orz
一些不需要link-cut操作的樹上路徑的題可以用樹鏈剖分做,常數比lct小多了。//upd:所以這已經不是動態樹了囧。。。。。標題我就不改了。。。。。。還好原來機智打了個「霧」
學習了下hld(樹鏈剖分),嗯,挺簡單的。hld可以在樹中的操作有很多,hld可以說只是一種概念結構,它可以套很多其它的資料結構來進行操作,比如我現在只要求路徑最值和求和,那麼套線段樹就行了;如果我要求第k大,可以套splay和主席樹(這個不知道),也可以套分塊(不會,分塊以後學,必須學。。)但是我覺得,樹剖比lct還要難寫。。我lct一下就能寫出來了。。可是lct的常數,不忍直視。。概念:
重兒子:num[u]為v的子節點中num值最大的,那麼u就是v的重兒子。
輕兒子:v的其它子節點。
重邊:點v與其重兒子的連邊。
輕邊:點v與其輕兒子的連邊。
重鏈:由重邊連成的路徑。
輕鏈:輕邊。
剖分後的樹有如下性質:
性質1:如果(v,u)為輕邊,則siz[u] * 2 < siz[v];
性質2:從根到某一點的路徑上輕鏈、重鏈的個數都不大於logn。
我們來說他怎麼在路徑操作吧:
下邊是乙個例圖:黑邊為重邊,帶紅點的為重邊組成的鏈的最頂點(沒有重邊的點最頂點就是它自己),藍字為重邊的序號
哈哈,發現了什麼嗎?
重鏈的序號是連續的。 不會有兩條重鏈相交~~
哈哈?套各種樹維護啦~~~。
我們在這個連續的區間操作就行了,然後不斷向根走,直到走到最頂點相交(這裡不是重鏈相交,是最頂點相交,即一條輕邊和重邊的交點)
向上走的時候,不是乙個個走,那麼效率大大提高啦~。。這就是樹鏈剖分的主體思想。
我們在這些鏈(或點)重新標號後,用各種資料結構維護資訊。
求樹剖需要維護的域很簡單,兩個深搜搞定,大家自己想吧,我不說了(要是想不通,,點這:
例題:基於點分類:【bzoj】1036: [zjoi2008]樹的統計count(lct/樹鏈剖分)
#include #include using namespace std;#define dbg(x) cout << #x << "=" << x << endl
#define read(x) x=getint()
#define print(x) printf("%d", x)
#define lc x<<1
#define rc x<<1|1
#define lson l, m, lc
#define rson m+1, r, rc
#define mid (l+r)>>1
const int oo=~0u>>1;
inline int getint()
const int n=30010, m=100005;
int ihead[n], inext[m], to[m], cnt, n, m;
int top[n], son[n], fa[n], dep[n], sz[n], id[n], a[n], b[n], tot;
int l, r, key;
struct node t[n*50];
inline const int max(const int& a, const int& b)
inline void pushup(const int &x)
void dfs1(const int &u)
}void dfs2(const int &u, const int &tp)
void build(const int &l, const int &r, const int &x)
int m=mid;
build(lson); build(rson);
pushup(x);
}void update(const int &l, const int &r, const int &x)
int m=mid;
if(l<=m) update(lson);
if(mdep[y]) swap(x, y);
l=id[x], r=id[y];
return max(ret, getmax(1, n, 1));
}inline int query(int x, int y)
inline void add(const int &u, const int &v)
int main()
inline int getint()
const int n=50010, oo=~0u>>1;
struct ed e[n];
int ihead[n], inext[n<<1], to[n<<1], cnt;
int fa[n], sz[n], son[n], top[n], dep[n], id[n], mx[n*5], num[n], tot, l, r, key, n;
inline void pushup(const int &x)
void build(const int &l, const int &r, const int &x)
int m=mid;
build(lson); build(rson);
pushup(x);
}void update(const int &l, const int &r, const int &x)
int m=mid;
if(l<=m) update(lson); if(msz[son[u]]) son[u]=v; }}
void dfs2(const int &u, const int &tp)
inline int getmax(int x, int y)
inline void add(const int &u, const int &v)
int main()
build(2, n, 1);
for(ch=getchar(); ch'z'; ch=getchar());
while(ch!='d')
else printf("%d\n", getmax(a, b));
for(ch=getchar(); ch'z'; ch=getchar());
} }return 0;
}
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...
樹鏈剖分 BZOJ3589 動態樹
time limit 30 sec memory limit 1024 mb submit 543 solved 193 submit status discuss 別忘了這是一棵動態樹,每時每刻都是動態的.小明要求你在這棵樹上維護兩種事件 事件0 這棵樹長出了一些果子,即某個子樹中的每個節點都會長...
樹鏈剖分 樹剖換根
這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...