第一次寫LCT,先來兩道模板題,因爲上個月被三道區間維護的超級工業的splay搞傻了,吸取了很多教訓(見前幾篇博客),所以LCT寫得很順啊,這裏要感謝hzwer的題解。
BZOJ 2049:給出一顆樹,每次加一條邊或者刪除一條邊,詢問兩點的連通性。
這是一眼題,最裸的,純天然的LCT,如果你不會先去看論文吧。半年以前看LCT感覺是一個非常高端的東西,當時反正看不懂,而TTY又有LCT爺的稱號,寫LCT都不用調,自然是伏地%。今天寫了寫發現還好。
廢話不多說,查連通性就判斷兩個點的樹的根是不是相等就可以了。
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
x=0;int flag=0;char c;
while(c=getchar())
{
if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
}
const int MAXN=10010;
int n,m;
char s[10];
struct Link_Cut_Tree{
int fa[MAXN],son[MAXN][2],rev[MAXN],top,st[MAXN];
Link_Cut_Tree(){
memset(fa,0,sizeof(fa));
memset(son,0,sizeof(son));
memset(rev,0,sizeof(rev));
top=0;
}
bool isroot(int x)
{
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void rotate(int x)
{
int y=fa[x],d=(x==son[y][1]);
if(fa[y]&&!isroot(y))
{
son[fa[y]][son[fa[y]][1]==y]=x;
}
assert(x!=fa[y]);
fa[x]=fa[y];
assert(y!=x);
fa[y]=x;
son[y][d]=son[x][d^1];
if(son[x][d^1])fa[son[x][d^1]]=y;
son[x][d^1]=y;
}
void update_rev(int now)
{
rev[now]^=1;
swap(son[now][0],son[now][1]);
}
void push_down(int x)
{
if(rev[x])
{
rev[x]^=1;
update_rev(son[x][0]);
update_rev(son[x][1]);
}
}
void splay(int x)
{
st[++top]=x;
int tmp=x;
while(!isroot(tmp))
{
st[++top]=fa[tmp];
tmp=fa[tmp];
}
while(top)
{
push_down(st[top]);
top--;
}
while(!isroot(x))
{
int y=fa[x];
if(isroot(y))
{
rotate(x);
break;
}
rotate(x);
}
}
void aksess(int x)
{
int tmp=0;
while(x)
{
splay(x);
son[x][1]=tmp;
tmp=x;
x=fa[x];
}
}
void beroot(int x)
{
aksess(x);
splay(x);
update_rev(x);
}
void link(int x,int y)
{
beroot(x);
fa[x]=y;
splay(x);
}
void cut(int x,int y)
{
beroot(x);
aksess(y);
splay(y);
son[y][0]=fa[x]=0;
}
int find(int x)
{
aksess(x);
splay(x);
int tmp=x;
while(tmp)
{
if(!son[tmp][0])
return tmp;
tmp=son[tmp][0];
}
}
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("cave.in","r",stdin);
freopen("cave.out","w",stdout);
#endif
Read(n);Read(m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[0]=='Q')
{
int a,b;
Read(a);Read(b);
if(LCT.find(a)==LCT.find(b))
puts("Yes");
else
puts("No");
}
else if(s[0]=='D')
{
int a,b;
Read(a);Read(b);
LCT.cut(a,b);
}
else
{
int a,b;
Read(a);Read(b);
LCT.link(a,b);
}
}
}
BZOJ 2157:給出一棵樹,每次可以更改邊權,路徑邊權置負,查詢路徑權值和,路徑權值最大值,路徑權值最小值。
雖然這一題既沒有加邊也沒有刪邊,但是爲了多A一道題而且能練習一下打改值標記的splay,就沒有寫樹鏈剖分了。
LCT是不能維護邊的權值的,我們考慮把邊建成點,第i號邊就是第n+i號點,然後也是模板題了。這裏樹上路徑問題與區間維護問題有一點點不同,區間維護的splay要設兩個虛點,取出區間就是把一個點splay到根,另一個點splay到根的右兒子,取出右兒子的子樹。而LCT直接換根,aksess,再splay就可以了。
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define lc son[now][0]
#define rc son[now][1]
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
x=0;int flag=0,sgn=1;char c;
while(c=getchar())
{
if(c=='-')sgn=-1;
else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
else if(flag)break;
}
x*=sgn;
}
const int MAXN=200011;
char s[10];
int n,m,f[MAXN];
struct Link_Cut_Tree{
int fa[MAXN],son[MAXN][2],st[MAXN],top,mx[MAXN],sum[MAXN],mn[MAXN],v[MAXN];
bool rev[MAXN],neg[MAXN];
Link_Cut_Tree(){
top=0;
memset(rev,0,sizeof(rev)); memset(neg,0,sizeof(neg));
memset(fa,0,sizeof(fa)); memset(son,0,sizeof(son));
memset(mx,-INF,sizeof(mx));memset(mn,INF,sizeof(mn));
memset(sum,0,sizeof(sum)); memset(v,0,sizeof(v));
}
void update_rev(int now)
{
rev[now]^=1;
swap(lc,rc);
}
void update_neg(int now)
{
neg[now]^=1;
sum[now]*=-1;
swap(mx[now],mn[now]);
mx[now]*=-1;
mn[now]*=-1;
v[now]*=-1;
}
void update(int now)
{
sum[now]=sum[lc]+sum[rc]+v[now];
mn[now]=min(mn[lc],mn[rc]);
mx[now]=max(mx[rc],mx[lc]);
if(now>n)
{
mn[now]=min(mn[now],v[now]);
mx[now]=max(mx[now],v[now]);
}
}
void push_down(int now)
{
if(rev[now])
{
rev[now]^=1;
update_rev(lc);
update_rev(rc);
}
if(neg[now])
{
neg[now]^=1;
update_neg(lc);
update_neg(rc);
}
}
bool isroot(int x)
{
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void rotate(int x)
{
int y=fa[x],d=(son[y][1]==x);
if(fa[y]&&!isroot(y))
son[fa[y]][son[fa[y]][1]==y]=x;
fa[x]=fa[y];
fa[y]=x;
son[y][d]=son[x][d^1];
if(son[x][d^1])
fa[son[x][d^1]]=y;
son[x][d^1]=y;
update(y);update(x);
}
void splay(int x)
{
top=0;
st[++top]=x;
int tmp=x;
while(!isroot(tmp))
{
st[++top]=fa[tmp];
tmp=fa[tmp];
}
while(top)
push_down(st[top--]);
while(!isroot(x))
rotate(x);
}
void aksess(int x)
{
int t=0;
while(x)
{
splay(x);
son[x][1]=t;
update(x);
t=x;x=fa[x];
}
}
void beroot(int x)
{
aksess(x);
splay(x);
update_rev(x);
}
void link(int x,int y)
{
beroot(x);
fa[x]=y;
splay(x);
}
void split(int x,int y)
{
beroot(x);
aksess(y);
splay(y);
}
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
#endif
scanf("%d",&n);
int id=n;
for(int i=1;i<n;i++)
{
//DEBUG("%d\n",i);
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
a++,b++;
LCT.v[++id]=c,LCT.update(id);//插節點注意update
f[i]=id;
LCT.link(a,id),LCT.link(b,id);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
int x,y;
Read(x),Read(y);
if(s[0]=='C')
{
LCT.splay(f[x]);
LCT.v[f[x]]=y;
LCT.update(f[x]);
}
else if(s[0]=='N')
{
LCT.split(x+1,y+1);
LCT.update_neg(y+1);
}
else if(s[0]=='S')
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.sum[y+1]);
}
else if(s[1]=='A')
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.mx[y+1]);
}
else
{
LCT.split(x+1,y+1);
printf("%d\n",LCT.mn[y+1]);
}
}
}