Brief description :
… 动态维护一组森林,要求支持以下操作:
- Link(a, b) 如果 a,b 不在同一颗子树中,则通过在 a,b 之间连边的方式,连接这两棵子树。
- Cut(a, b) 如果 a,b 在同一颗子树中、且 a != b,则将 a 视为这棵子树的根之后,切断 b 与其父亲结点的连接。
- Modify(w, a, b) 如果 a, b 在同一颗子树中,则将 a, b 之间路径上所有的点权增加 w。
- Query(a, b) 如果 a, b 在同一颗子树中,返回 a, b 之间路径上点权的最大值。
Analysis :
。。Link-Cut Tree 的核心操作 。。Access()。。(只有调用这个过程会改变边的虚实关系 (Solid / Dashed) )。。
。。介于 Splay 保存路径的特殊方式,通常。。要将需要操作的节点 Splay() 到根(以获得其对整棵子树的完整信息)之后才可以对其进行进一步的操作 .. .
这里需要引入 rev 标记进行修改树根的操作(只存在于作用于无根树的题目中,例如 SPOJ 4155. OTOCI …)
void Link(int x, int y){
if (Root(x) == Root(y)){
puts("-1");
}
else {
Evert(x), Splay(x), p[x] = y, Access(x);
}
}
void Cut(int y, int x){
if (x == y || Root(x) != Root(y)){
puts("-1");
}
else {
Evert(y), Splay(y), Access(x), Splay(x);
p[lx] = p[x], rt[lx] = true, p[x] = lx = 0;
//Update(x);
}
}
。再以此题为例。。
对于得到一条路径的做法,先 一个 Access() 过程访问其中的一个点,再通过 一个修改的 Access() 过程 访问另一个点,
在第二次 Access() 的最后阶段会通过 p[] 指针得到这两点之间的 LCA … (Orz)
(。。交换 Access() 的顺序不影响。。)
然后使用 .. max(w1[rx], w0[x], w1[y]) .. 就可以得到这条路径上点权的最大值。(如果是边权系列问题,中间的 w0[x] 会消失。。。。 )
void Query(int x, int y){
if (Root(x) != Root(y)){
puts("-1");
}
else {
Access(y); y = 0; do{
Splay(x), Release(x); if (!p[x]) OT(max(w1[rx], w0[x], w1[y]));
rt[rx] = true, rt[rx = y] = false, Update(x);
x = p[y = x];
} while (x);
}
}
对于修改操作,我们需要再引入一个 delay 标记。
inline void Inc(int x, int d){
if (x == 0) return;
w0[x] += d, w1[x] += d, delay[x] += d;
}
于是得到 Modify() 的过程。。。
void Modify(int x, int y, int w){
if (Root(x) != Root(y)){
puts("-1");
}
else {
Access(y); y = 0; do{
Splay(x), Release(x); if (!p[x]) Inc(rx, w), w0[x] += w, Inc(y, w);
rt[rx] = true, rt[rx = y] = false, Update(x);
x = p[y = x];
} while (x);
}
}
。。各种维护就可以了。。。。
( .. HDOJ Rank I . 593ms 达成 .. )
Further discussion :
Link-Cut Tree 的先修技能是 Splay,推荐入门题。。
SGU 187. Twist and whirl — want to cheat .. .
之后做这题之前一定要有同时维护向上向下传递的经验。
(自顶向下下放 (Release()) 的标记 (rev, delay),和自底向上维护 (Update()) 的关键字 (w0, w1))。。
可以参见那两道 S 级数据结构 .. .
NOI 2005. 维修数列 。。和。。
SPOJ 1557. Can you answer these queries II。。
额。。。补充一些问题,
首先,各个子过程需要做到 ———— 各司其职。。
这里的含义是尽量不要做额外「画蛇添足」的事情,例如 。。
Access() 操作之后紧接着 Splay() 到根节点 (直接固化在 Access() 里面)。。。。。 不必要。。。你可以通过 调用 _Access(x) (加前缀下划线表示一个返回版本。。)结束然后返回其最后访问到的结点(也就是此时的树根。)。。对这个结点进行操作即可。。Update() 过程里面嵌一个 Release() 。。。(这个。。因为此题中维护的 w1 信息,左右子树是否交换对其不造成影响。。显然可以去掉。。( GSS 6 )。。)Rotate() 操作里面不停的 Update(), Release(), Update(), Release() 。。.. .- …
- .
(额,最后说一下由于我一直使用单旋 Splay。。 Splay() 过程被写的只有一行。。没有对旋转方向的判断所以是需要写一部分标记下放到 Rotate() 里面的。。)
(。。Unsolved Problem .. 。。)
如果你是「数据结构控」。。对这几个非常感兴趣,我猜你可能会喜欢这个。。.[挖坑]动态树与树链剖分.. .
我收集了一些资源 (Resource) 和习题 (Exercise)
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define REP(i, n) for (int i=0;i<int(n);++i)
#define FOR(i, a, b) for (int i=int(a);i<int(b);++i)
#define REP_1(i, n) for (int i=1;i<=int(n);++i)
#define FOR_C(i, a, b) for (int b____=int(b),i=int(a);i<b____;++i)
#define REP_1_C(i, n) for (int n____=int(n),i=1;i<=n____;++i)
#define DO(n) while(n--)
#define DO_C(n) int n____ = n; while(n____--)
template<class T> inline void RD(T &x){char c; for (c = getchar(); c < '0'; c = getchar()); x = c - '0'; for (c = getchar(); c >= '0'; c = getchar()) x = x * 10 + c - '0';}
inline int RD(){ int x; RD(x); return x;}
template<class T0, class T1> inline void RD(T0 &x0, T1 &x1){RD(x0), RD(x1);}
template<class T0, class T1, class T2> inline void RD(T0 &x0, T1 &x1, T2 &x2){RD(x0), RD(x1), RD(x2);}
template<class T> inline void OT(const T &x){printf("%d\n", x);}
template<class T> inline void RST(T &A){memset(A, 0, sizeof(A));}
template<class T0, class T1, class T2, class T3, class T4, class T5> inline void RST(T0 &A0, T1 &A1, T2 &A2, T3 &A3, T4 &A4, T5 &A5){RST(A0), RST(A1), RST(A2), RST(A3), RST(A4), RST(A5);}
template<class T> inline T max(T a, T b, T c){return max(max(a, b), c);}
/*--------*/
const int N = 300009, M = 2 * N;
int l[N], r[N], p[N], w0[N], w1[N], delay[N], rev[N]; bool rt[N];
// Link-cut tree
int hd[N], nxt[M], a[M], b[M];
// Adjacent list
int n;
#define lx l[x]
#define rx r[x]
// private:
inline void Inc(int x, int d){
if (x == 0) return;
w0[x] += d, w1[x] += d, delay[x] += d;
}
inline void Release(int x){
if (x == 0) return;
if (rev[x]){
swap(lx, rx);
rev[lx] ^=1, rev[rx] ^=1;
rev[x] = 0;
}
if (delay[x]){
Inc(lx, delay[x]), Inc(rx, delay[x]);
delay[x] = 0;
}
}
inline void Update(int x){
w1[x] = max(w0[x], w1[lx], w1[rx]);
}
inline void Set(int l[], int y, int x){
l[y] = x, p[x] = y;
}
#define z p[y]
inline void Rotate(int x){
int y = p[x];
if (!rt[y]) Release(z), Set(y == l[z] ? l : r, z, x);
else p[x] = z;
Release(y), Release(x);
if (x == l[y]) Set(l, y, rx), Set(r, x, y);
else Set(r, y, lx), Set(l, x, y);
if (rt[y]) rt[y] = false, rt[x] = true;
Update(y);
}
inline void Splay(int x){
while (!rt[x]) Rotate(x);
}
inline int Access(int x){
int y = 0; do{
Splay(x), Release(x);
rt[rx] = true, rt[rx = y] = false, Update(x);
x = p[y = x];
} while (x);
return y;
}
inline int Root(int x){
for (x = Access(x); Release(x), lx; x = lx);
return x;
}
inline void Evert(int x){
rev[Access(x)] ^= 1;
}
// public:
void Query(int x, int y){
if (Root(x) != Root(y))
puts("-1");
else {
Access(y); y = 0; do{
Splay(x), Release(x); if (!p[x]) OT(max(w1[rx], w0[x], w1[y]));
rt[rx] = true, rt[rx = y] = false, Update(x);
x = p[y = x];
} while (x);
}
}
void Link(int x, int y){
if (Root(x) == Root(y))
puts("-1");
else {
Evert(x), Splay(x), p[x] = y, Access(x);
}
}
// Set y as a root, Cut the connection between x and p[x] ..
void Cut(int y, int x){
if (x == y || Root(x) != Root(y))
puts("-1");
else {
Evert(y), Splay(y), Access(x), Splay(x);
p[lx] = p[x], rt[lx] = true, p[x] = lx = 0;
}
}
void Modify(int x, int y, int w){
if (Root(x) != Root(y))
puts("-1");
else {
Access(y); y = 0; do{
Splay(x), Release(x); if (!p[x]) Inc(rx, w), w0[x] += w, Inc(y, w);
rt[rx] = true, rt[rx = y] = false, Update(x);
x = p[y = x];
} while (x);
}
}
#define v b[i]
inline void dfs(int u = 1){
for(int i=hd[u];i;i=nxt[i]) if (!p[v]){
p[v] = u, dfs(v);
}
}
int main(){
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
while (scanf("%d", &n) != EOF){
// Initializing Phase
FOR_C(i, 2, n << 1){
RD(a[i], b[i]), a[i|1] = b[i], b[i|1] = a[i];
nxt[i] = hd[a[i]], hd[a[i]] = i; ++i;
nxt[i] = hd[a[i]], hd[a[i]] = i;
}
REP_1(i, n) RD(w0[i]), rt[i] = true;
p[1] = -1, dfs(), p[1] = 0;
//Interaction Phase
int a, b, cmd; DO_C(RD()){
RD(cmd, a, b); if (cmd == 1) Link(a, b);
else if (cmd == 2) Cut(a, b);
else if (cmd == 3) Modify(b, RD(), a);
else Query(a, b);
}
puts("");
// Rececling ...
RST(hd, p, l, r, delay, rev);
}
}
(
下一阶段的任务。。我觉得动态树我还没弄明白。。还想在花一段时间。。
先打算先写一下动态树的那个「正当用途」 ———— 优化网络流。。
之后。。
我就去解决掉 fhq 的 1、2、3 。。三个难题。。(> <)。。。。
)
--- UPD ---
现在的搞法。




Alca
Amber
Belleve Invis
Chensiting123
Edward_mj
Fotile96
Hlworld
Kuangbin
Liyaos
Lwins
LYPenny
Mato 完整版
Mikeni2006
Mzry
Nagatsuki
Neko13
Oneplus
Rukata
Seter
Sevenkplus
Sevenzero
Shirleycrow
Vfleaking
wangzhpp
Watashi
WJMZBMR
Wywcgs
XadillaX
Yangzhe
三途川玉子
About.me
Vijos

不是太懂access里的虚实边关系,以及splay维护边的方法,能解释一下吗。
代码被吞了。。求QQ传给我撒。。
251815992。。。
已加QQ,请回复QQ
能不能回答一下,我HDU4010哪里错了,为我指明错误,并告诉我怎么改,以及这样子改的理由。
谢谢。。
#include
#include
#include
#include
#include
#include
using namespace std;
inline int maxx(int a,int b){return a>b?a:b;}
inline int minn(int a,int b){return a>b?a:b;}
inline void swap(int &a,int &b){int t=a; a=b; b=t; return;}
#define maxn 1000010
struct edge_node{int next,y,d;}edge[maxn];
struct tree_node{int lc,rc,fa,d,val;
#define lc(x) tr[x].lc
#define rc(x) tr[x].rc
#define d(x) tr[x].d
#define fa(x) tr[x].fa
#define val(x) tr[x].val
}tr[maxn];
int first[maxn],len,n,m,t,limit;
inline int getint(){
char ch=getchar(); int tmp=0; bool bo=false;
for (;ch’9′; ch=getchar()) if (ch==’-‘) bo=true;
for (;ch>=’0’ && chlimit) val(x)=-1;
if (d(x)<=limit) val(x)=maxx(val(x),d(x));
return;
}
inline void l_rotate(int x){
int y,z; y=fa(x); z=fa(y);
if (lc(z)==y) lc(z)=x; if (rc(z)==y) rc(z)=x; fa(x)=z;
fa(lc(x))=y; rc(y)=lc(x); lc(x)=y; fa(y)=x;
updata(y);
return;
}
inline void r_rotate(int x){
int y,z; y=fa(x); z=fa(y);
if (lc(z)==y) lc(z)=x; if (rc(z)==y) rc(z)=x; fa(x)=z;
fa(rc(x))=y; lc(y)=rc(x); rc(x)=y; fa(y)=x;
updata(y);
return;
}
inline void splay(int x){
int y,z;
while (!isroot(x)){
y=fa(x); z=fa(y);
if (isroot(y)){
if (lc(y)==x) r_rotate(x);
else l_rotate(x);
}else{
if (lc(z)==y){
if (lc(y)==x) r_rotate(y),r_rotate(x);
else l_rotate(x),r_rotate(x);
}else{
if (lc(y)==x) r_rotate(x),l_rotate(x);
else l_rotate(y),l_rotate(x);
}
}
}
updata(x);
return;
}
inline void expose(int x){
int y=0;
while (x){
splay(x);
rc(x)=y; updata(x); y=x; x=fa(x);
}
return;
}
int main(){
freopen("hdu3804.in","r",stdin); freopen("hdu3804_1.out","w",stdout);
int i,x,y,z;
t=getint();
while (t–){
n=getint();
memset(first+1,255,n*sizeof(int)); len=0;
for (i=1; i<n; i++){
x=getint(); y=getint(); z=getint();
ins(x,y,z); ins(y,x,z);
}
val(0)=d(0)=-1; lc(0)=rc(0)=fa(0)=0;
val(1)=d(1)=-1; lc(1)=rc(1)=fa(1)=0;
dfs(1,0);
m=getint();
while (m–){
x=getint(); limit=getint();
expose(x); //splay(1); //updata(x);
int ans=val(lc(x));
if (d(x)<=limit) ans=maxx(ans,d(x));
printf("%d\n",ans);
}
}
return 0;
}
给一组我错的数据。
输入:
1
4
1 2 3
2 3 7
3 4 2
3
4 1
4 4
4 8
输出:
我的
-1
3
3
答案:
-1
3
7
[…] HDU 4010. Query on The Trees – 某岛 […]