您的位置:首页 > 其它

LinkCutTree 总结

2015-02-14 23:09 232 查看
最近学习了LinkCutTree,总结一下。

LinkCutTree是一种数据结构(是Tree Decomposition中的一种),她维护的一般是无向图(一个森林),支持连边、删边、链修改、链查询(点属于特殊的链,修改可以是单点修改、整链修改,查询可以是最值、和等)这四种操作。

中心思想是将边分类,一类边组成一些连续的链,每条链保存在一颗BST中(一般是Splay),BST中以点到根的距离为关键字(左边的点是右边的点的祖先),其它一些边连接这些链。(LinkCutTree是树链剖分(又叫轻重链剖分)的动态版本,并且更灵活),可以证明,LinkCutTree的各种操作都是均摊O(logn)的(渐进复杂度比树链剖分的O(log^2)还好,但是常数巨大,所以实测一般时间是树链剖分的1.5~2倍)。

上面的“链修改、链查询”指的是链上的点,如果要将对象改为边,可以为每条边建立一个边点,即若存在边(u,v),则新加一个点z代表边,将z连接u和v,z的点权就是(u,v)的边权,非边点的权设为-oo),然后对边权的统计就变成了对点权的统计(这是LCT中处理边信息的通法之一)。

#include <cstdio>
#include <iostream>
#define maxn 10010
using namespace std;

/*
我的代码风格:用数组模拟指针和结构体。
变量含义:
pnt[u] - path-parent of u in the tree
pre[u] - the father of u in the Splay
son[u][0] - the left child of u in the Splay
son[u][1] - the right child of u in the Splay
val[u] - the weight of u
sum[u] - the sum of weight of all the nodes in the subtree of u
siz[u] - the number of the nodes in the subtree of u
itg[u] - increasement tag ( the lazy tag )
rtg[u] - rotate tag ( the lazy tag )
*/
/*
模板功能:支持删边和连边,支持将一条链的点权做一个增量,支持查询一条链的点权和,判断两点是否再同一联通块中
因为是自己想的一个功能,所以没有地方交,不保证代码正确性。(重在理解)
代码中哪里不懂欢迎回复,代码丑别喷。
*/
namespace L {
int pnt[maxn], pre[maxn], son[maxn][2], val[maxn],
sum[maxn], siz[maxn], itg[maxn], rtg[maxn];

void update( int nd ) {
sum[nd] = val[nd] + sum[son[nd][0]] + sum[son[nd][1]];
}
void rotate( int nd, int d ) {
int p = pre[nd];
int s = son[nd][!d];
int ss = son[s][d];

son[nd][!d] = ss;
son[s][d] = nd;
if( p ) son[p][ nd==son[p][1] ] = s;
else pnt[s] = pnt[nd];

pre[nd] = s;
pre[s] = p;
pre[ss] = nd;

update( nd );
update( s );
}
void pushdown( int nd ) {
if( rtg[nd] ) {
int &ls = son[nd][0], &rs = son[nd][1];
swap(ls,rs);
rtg[ls] ^= 1;
rtg[rs] ^= 1;
rtg[nd] = 0;
}
if( itg[nd] ) {
int ls = son[nd][0], rs = son[nd][1];
int delta = itg[nd];
itg[ls] += delta;
itg[rs] += delta;
val[ls] += delta;
val[rs] += delta;
sum[ls] += siz[ls]*delta;
sum[rs] += siz[rs]*delta;
itg[nd] = 0;
}
}
void big_push( int nd ) {
if( pre[nd] ) big_push(pre[nd]);
pushdown(nd);
}
void splay( int nd, int top=0 ) {
big_push(nd);
while( pre[nd]!=top ) {
int p = pre[nd];
int nl = nd==son[p][0];
if( pre[p]==top ) {
rotate( p, nl );
} else {
int pp = pre[p];
int pl = p==son[pp][0];
if( nl==pl ) {
rotate( pp, pl );
rotate( p, nl );
} else {
rotate( p, nl );
rotate( pp, pl );
}
}
}
}
void access( int nd ) {
int u = nd;
int v = 0;
while( u ) {
splay( u );
int s = son[u][1];
pre[s] = 0;
pnt[s] = u;
pre[v] = u;
son[u][1] = v;
update( u );
v = u;
u = pnt[u];
}
splay( nd );
}
int findroot( int nd ) {
while( pre[nd] ) nd=pre[nd];
while( pnt[nd] ) {
nd = pnt[nd];
while( pre[nd] ) nd=pre[nd];
}
return nd;
}
void makeroot( int nd ) {
access( nd );
rtg[nd] ^= 1;
}
bool sameroot( int u, int v ) {
return findroot(u)==findroot(v);
}
void link( int u, int v  ){
makeroot(u);
makeroot(v);
pnt[u] = v;
}
void cut( int u, int v ) {
makeroot(u);
access(v);
pnt[u] = 0;
pre[u] = 0;
son[v][0] = 0;
update( v );
}
void up_val( int u, int v, int delta ) {
makeroot(u);
access(v);
val[v] += delta;
sum[v] += siz[v]*delta;
itg[v] += delta;
}
int qu_sum( int u, int v ) {
makeroot(u);
access(v);
return val[v]+sum[son[v][0]];
}
};
/*
int main() {
L::link(1,2);
L::link(2,3);
L::link(3,4);
L::up_val(1,3,3);
L::up_val(2,4,-3);
printf( "%d\n", L::qu_sum(1,1) );
printf( "%d\n", L::qu_sum(2,2) );
printf( "%d\n", L::qu_sum(3,3) );
printf( "%d\n", L::qu_sum(4,4) );
printf( "%d\n", L::qu_sum(2,3) );
}
*/
int main() {
L::link(1,2);
L::link(2,3);
L::link(3,4);
L::up_val( 1, 4, 5 );
L::cut(2,3);
printf( "%d\n", L::qu_sum(1,2) );
printf( "%d\n", L::qu_sum(3,4) );
printf( "%d\n", L::sameroot(2,3) );
}


View Code

推荐学习资料:

杨思雨 《伸展树的基本操作与应用》

杨哲 《QTREE解法的一些研究》
http://blog.csdn.net/d891320478/article/details/9181385
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: