Update on 2017/4/17
多坑慎入……
这篇文章不仅没有讲什么虚实边,作者还是个智障。
大家如果要看的话建议先去参考其他人的TAT
窝理解清楚再补。。。QAQ
这次不是在很多人的帮助下……而是自己调的代码、、、
略微看了看自己LCT掉进去的几个坑、、、
略微介绍一下:
LCT的类似于树链剖分,只不过树链剖分是静态的,而LCT是动态的。
LCT用Splay来维护每一条重链,而且在初始情况下,LCT是没有轻重链之分的。
请注意,这里Splay维护的是按照深度递增的顺序来进行排序的。
找到一张图是这样的:
把这个树变成LCT之后……
核心操作:
Access(v):
访问一个节点,其作用是把该点到LCT的根的路径都变成重链,由于我们维护LCT的时候是用的Splay,我们要把v这个节点Splay到根,也就是Splay到LCT的总根,这是一切操作的基础。
Splay操作:
把一个点伸展到它所在重链的根。
请注意一件事情,也是我最初极为困惑的事情。
在我们学习Splay的时候,我们的根节点的父亲是0,然而这里一条重链的根节点的父亲显然不是0,为了区分轻边重链,我们会使得根节点的父亲的左右儿子并不是根节点。
这里我找了网上的一张图。
这张图片里面,7的父亲是1,然而1是在浅红色重链里面的,即它是Splay的最左边节点(因为深度递增),所以它是没有左右儿子的,对吧?然而:7的父亲是1,这点却是一个不争的事实。
也就是说,我们通过什么来判断这个点是否是维护这条重链的Splay的根呢?
显然,我们只要看父亲节点的左右儿子里有没有它即可。
FRT(find root寻找原来的树的根)
请注意这里的root并不是LCT的根,而是原来那个树的根,切记切记。
大概我们来想想就是这样:
首先,根节点是深度最小的点。
所以我们考虑Access(v),v这里是任意一个节点。
然后Splay(v),走到它的最左端即可。
MRT(make root换根操作)
请注意,这里的换根操作也是原来的树的根。
所以说我们只需要这样就行了。
网上的图片……
我们把u换成v,也就意味着我们在保证其树的父子关系不变的情况下,使得v的深度最小。
那么也就意味着,我们只需要先Access(v),Splay(v),然后打个翻转标记即可。
这里要注意的一点是,如果询问跟固定一个根有关,在询问的时候一定要记住把根换回来……
Findroot可以找到当前原树的根,也就意味着我们把u换成v之后,Findroot的返回值就是v了。
Cut操作:
切断一条边。
显然在Cut的时候不是简单的找到两个点然后一划就行了的。
我们设Cut(u,v)。
MRT(u),Access(v),Splay(v)。
然后我们就会知道u一定在v的左儿子的位置,然后把v的左儿子标成0,u的父亲标成0即可。
Link操作:
连接一条边。
在连接之前我们肯定要先断掉之前的边『否则你的树形结构就被破坏了』。
我们考虑已经Cut完毕了,现在要连接(u,v),那么只需要:
MRT(u),t[u].par = v即可。
当然了……你随便把哪个当作父亲都是可以的。
模板题是弹飞绵羊。
问题在于……
询问的时候能处理什么?
至少能处理一种东西:
节点深度。
Access(v),Splay(v),return t[lc].sz即可。
BZOJ2002
『题意』
有一群弹簧(1~n),它们喜欢弹飞绵羊,每个弹簧可以让绵羊向后弹Ai格,如果绵羊被弹到超过n的范围,就算是被弹飞了,现在可以更改弹簧的后弹绵羊的能力,而有一个人会在某个弹簧上放一个绵羊,给出绵羊经过几次会被弹飞。
分析:
考虑我们虚拟一个节点标号为n+1,如果第i个弹簧能将绵羊弹飞到i + j,那么i - > min(i + j,n + 1)连边。
更改弹簧能力也就意味着Link&&Cut操作,放羊操作相当于询问节点的深度。
这题就这么过去了……第一次写的话调了好长时间。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define Repl(i,n) for(int i = 1,ls; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define u t[x]
#define debug puts("**")
#define debug2 puts("404 NOT FOUND")
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define v edge[i].to
#define ulfc t[u.lc]
#define o t[y]
#define urtc t[u.rc]
#define p u.par
const int N = 200005;
using namespace std;
const int inf = 1 << 30;
typedef long long ll;
int read(){
char ch = getchar();
while(ch < '0' || ch > '9')ch = getchar ();
int x = 0;
while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
return x;
}
int n,m;
struct LCT{
int ch[2],par,sz;
bool rev;
LCT(){sz = 1,ch[0] = ch[1] = par = 0;}
void Rev(){rev ^= 1;swap(lc,rc);}
}t[N];
int sgn(int x){return t[p].lc == x ? 0 : t[p].rc == x ? 1 : -1;}
void sc(int x,int y,bool ty){u.tc = y,o.par = x;}
void Upd(int x){if(x)u.sz = ulfc.sz + urtc.sz + 1;}
void Dw(int x){
if(u.rev)
ulfc.Rev(),urtc.Rev(),u.rev ^= 1;
}
void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
void Rot(int x,bool ty){
int y = p;
//printf("ROTING:%d %d %d %d\n",x,y,o.par,ty);
if(~sgn(y))sc(o.par,x,sgn(y));
else p = o.par;
sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
int Splay(int x){
Fix(x);
int d0,d1,y;
//printf("x : %d x的父亲%d\n",x,p);
//debug2;
while((~(d0 = sgn(x)))){
if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1);
else Rot(x,d0);
}
//Rep_0(i,n + 2)
// printf("~~~%d %d %d %d~~\n",i,t[i].par,t[i].ch[0],t[i].ch[1]);
return Upd(x),x;
}
int nxt[N];
int Acs(int tx){
// puts("NOW : ACS");
for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y;
// puts("ACS : ENDED");
// Rep_0(i,n + 2)
// printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz);
return Splay(tx);
}
void MRT(int x){x = Acs(x);u.Rev();}
void Link(int x,int y){MRT(x),p = y;}
void DB(){
puts("DEBUGING--------------------------------------------");
Rep_0(i,n + 2)
printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz);
puts("END-------------------------------------------");
}
void Cut(int x,int y){MRT(x),y = Acs(y),o.lc = 0,u.par = 0;}
int main (){
// freopen("a.txt","r",stdin);
t[0].sz = 0;//TMD初始化
n = read();
Repl(i,n)
nxt[i] = read(),t[i].par = min(i + nxt[i],n + 1),nxt[i] = min(nxt[i] + i,n + 1);
// DB();
//Rep_0(i,n + 2)
// printf("%d %d\n",i,t[i].par);
m = read();
// DB();
Rep(i,m)
{
int op = read(),x = read();
// puts("-------------------------------------------------------");
x ++;
if(op & 1){
// DB();
MRT(n + 1);//为什么不换根呢少年!!!!!!
x = Acs(x);
printf("%d\n",ulfc.sz);
// Rep_0(i,n + 2)
// printf("lalala: %d %d\n",i,t[i].sz);
}
else {
int k = read();
// DB();
x = Acs(x);//printf("CUT:%d %d\n",x,x + k);
// DB();
// printf("%d NXT:%d\n",x,nxt[x]);
Cut(x,nxt[x]);//你要CUT 的不是你的左儿子啊!!!! nxt数组爆了啊!!!!
// printf("%d NXT : %d\n",x,nxt[x]);
// DB();
Link(x,min(x + k,n + 1));
nxt[x] = min(x + k,n + 1);
// printf("%d NXT : %d\n",x,nxt[x]);
// DB();
// Rep_0(i,n + 2)
// printf("***%d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1]);
// printf("%d %d\n",x,x + k);
}
}
return 0;
}
/*
10
4 5 4 3 1 3 1 1 1 3
5
2 0 2
2 7 1
2 5 7
1 6
2 5 6
10
6 7 10 2 5 5 4 4 10 7
10
2 2 1
2 2 4
*/
大致错误犯了四个,第一个是忘记设置t[0]的sz为0,第二个是在询问的时候忘了换根,而至于第三个……
Cut错边了!!!!
Cut的时候一定要清楚你是Cut什么边……显然我们是找到两个相邻的节点去Cut,那么我们需要存一下上一次的x能被弹飞到哪里去。
第四个是比较2的错误,就是我nxt数组一开始取值为(i + nxt[i]),然而实际上那样会完美RE。
应该改为min(i + nxt[i],n + 1)。
模板就这样说完了。
部分转载自百度文库的一篇文章叫做
Link - Cut- Trees,写的很详细『当然可能有些人认为很废话』,感谢。
题库网站的话:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25242#overview
略微注意一下就是sone1是TopTree……
这网站好多错……看着做吧#233
略微调了一道题……
因为边带负权而我的快读一般都不带负数读入……
然后我就愉快地狗带了。
http://www.lydsy.com/JudgeOnline/problem.php?id=2157
BZOJ2157。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define Repl(i,n) for(int i = 1,ls; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define u t[x]
#define debug puts("**")
#define debug2 puts("404 NOT FOUND")
#define DEBUG puts("FREOPENING")
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define v edge[i].to
#define ulfc t[u.lc]
#define o t[y]
#define urtc t[u.rc]
#define p u.par
using namespace std;
const int inf = 1 << 30;
const int N = 200005;
typedef long long ll;
int read(){
char ch = getchar();
while((ch < '0' || ch > '9')&& ch != '-')ch = getchar ();
int x = 0,flag = 1;
if(ch == '-')flag = -1,ch = getchar();
while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
return x * flag;
}
int n,m,ed[N],idx;
struct LCT{
int w,Max,Min,ch[2],par;
ll sum;
bool rev;
bool tag;
LCT(){w = sum = tag = 0;Max = -inf,Min = inf;}
void Rev(){rev ^= 1;swap(lc,rc);}
void TagR(){
tag ^= 1;swap(Max,Min),w = -w,sum = - sum,Max = -Max,Min = -Min;
}
void TagW(int ps){
sum -= w,w = ps,sum += w;
}
}t[N << 2];
void DB(){/*puts("DEBUGING--------------------------------------------------");*/Rep_0(x,idx + 1)printf("x: %d sum: %lld w: %d lc: %d rc: %d p: %d\n",x,u.sum,u.w,u.lc,u.rc,p);}
void Upd(int x){
u.sum = ulfc.sum + urtc.sum + u.w;
u.Max = max(ulfc.Max,urtc.Max);
if(x > n)u.Max = max(u.w,u.Max);
u.Min = min(urtc.Min,ulfc.Min);
if(x > n)u.Min = min(u.Min,u.w);
}
void Dw(int x){
if(u.rev){
ulfc.Rev(),urtc.Rev();u.rev ^= 1;
}
if(u.tag){
ulfc.TagR(),urtc.TagR();u.tag ^=1;
}
}
int sgn(int x){return t[p].lc == x ? 0 :t[p].rc == x ? 1 : -1;}
void sc(int x,int y,bool ty){u.tc = y;o.par = x;}
void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
void Rot(int x,bool ty){
int y = p;
if(~sgn(y))sc(o.par,x,sgn(y));
else p = o.par;
sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
int Splay(int x){
Fix(x);
int d0,d1,y;
while((~(d0 = sgn(x)))){
if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1);
else Rot(x,d0);
}
return Upd(x),x;
}
int Acs(int tx){
for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y;
return Splay(tx);
}
void MRT(int x){Acs(x);u.Rev();}// 先Rev?
void link(int x,int y){MRT(x),p = y;}
int main (){
// freopen("lv.txt","r",stdin);
// freopen("outp.txt","w",stdout);
n = read();
idx = n;
Rep(i,n - 1){
int a,b,c;
ed[i] = ++ idx;
a = read() + 1,b = read() + 1,c = read();
link(a,idx),link(b,idx);
t[idx].w = c;
t[idx].Max = c;
t[idx].Min = c;
t[idx].sum = c;
}
m = read();
char op[5];
Rep(i,m){
scanf("%s",op + 1);
int a = read(),b = read();
if(op[1] == 'C')
Splay(ed[a]),t[ed[a]].w = b,Upd(ed[a]);
else if(op[1] == 'N'){ //
a ++ ,b ++;
MRT(a);
Acs(b);
t[b].TagR();
}
else{ // Sum
a ++ , b ++;
MRT(a);
Acs(b);
if(op[1] == 'S')//Sum
printf("%lld\n",t[b].sum);
else if(op[2] == 'I')// Min
printf("%d\n",t[b].Min);
else if(op[2] == 'A')
printf("%d\n",t[b].Max);
}
}
return 0;
}
/*
5
0 1 32
0 4 45
0 3 37
1 2 76
3
N 0 3
SUM 0 1
MIN 0 2
*/
其实这道题没必要用LCT,然而就是想练模板……
然后还有一道题。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define u t[x]
#define o t[y]
#define p t[x].par
#define v edge[i].to
#define ulfc t[u.lc]
#define urtc t[u.rc]
const int N = 10005;
using namespace std;
const int inf = 1 << 30;
typedef long long ll;
inline int read(){
char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-')ch = getchar ();
int x = 0;
bool flag = 1;
if(ch == '-')ch = getchar(),flag = 0;
while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
return flag ? x : -x;
}
int n,m;
struct LCT{int ch[2],par;bool rev;void Rev(){rev ^= 1;swap(lc,rc);}}t[N];
inline int sgn(int x){return t[p].ch[0] == x ? 0: t[p].ch[1] == x ? 1 : -1;}
inline void sc(int x,int y,bool ty){o.par = x;u.tc = y;}
inline void Upd(int x){;}
inline void Rot(int x,bool ty){
int y ;
if(~sgn(y = p))sc(o.par,x,sgn(y));
else p = o.par;
sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
inline void Dw(int x){
if(u.rev)ulfc.Rev(),urtc.Rev(),u.rev ^= 1;
}
inline void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
int Splay(int x){
Fix(x);
int d0,d1,y;
while(~(d0 = sgn(x))){
if(~(d1 = sgn(y = p)))
Rot(d0 ^ d1 ? x : y, d0),Rot(x,d1);
else Rot(x,d0);
}
return Upd(x),x;
}
int Acs(int tx){for(int x = tx,y = 0 ;x;Upd(y = x),x = p)Splay(x),u.rc = y;return Splay(tx);}
inline void MRT(int x){Acs(x);u.Rev();}
inline void Link(int x,int y){MRT(x);p = y;}
inline void Cut(int x,int y){MRT(x);Acs(y);o.lc = 0,u.par = 0;}
inline bool Find(int x,int y){while(p)x = p;while(o.par)y = o.par;return x == y;}
int main (){
n = read();
m = read();
Rep(i,m)
{
char op[15];
scanf("%s",op + 1);
int a = read(),b = read();
if(op[1] == 'Q')
puts(Find(a,b) ? "Yes" : "No");
else if(op[1] == 'C')
Link(a,b);
else if(op[1] == 'D')
Cut(a,b);
}
return 0;
}
洞穴勘测Cave……
很水的一道题……也就是练模板了。
下面开始做真正的LCT吧,另开一文好了。