題目鏈接
有兩棵上限是N個點的樹(有時候是森林),有兩個小夥伴分別操作自己的一棵樹,小夥伴A和小夥伴B,現在小夥伴們想知道對方的樹上能相互抵達的兩個結點,在本棵樹上是否也是可行的,也就是樹的同構“同分異構”(每棵子樹含有的結點相同,但是結構可能不同)。如果滿足,則爲YES,否則是NO。
有一種很神奇的方法,叫做給一條邊的兩個端點,同時異或一個值,那麼如果說這兩個端點在另一棵樹上也是相連的,那麼表明了他們的異或和應該是爲0的,那麼如果不在同一個樹中的話,兩棵樹上的點的總的異或和應該都是有剩餘的,所以我們可以rand()隨機數來產生這個異或值,然後利用一個動態樹形數據結構來維護,就是LCT了。
LCT可以很好的維護一條鏈上的信息,如果要維護一棵樹呢?我們知道有實虛邊這一說法。實邊中的左兒子是它的父親結點,實邊中的右兒子是它的子結點,但是還有一些實虛邊,虛邊的值我們依然也要去維護下來,什麼時候會產生虛邊?只有在access()函數中會產生虛邊的變化,因爲它要將這個點到根結點的這條鏈給拉出來,所以會使得虛邊實邊的變化。
我們如果用other來記錄虛邊上的值的話,會有這樣的情況:
void access(int x)
{
int y = 0;
while(x)
{
Splay(x);
other[x] ^= s[c[x][1]];
other[x] ^= s[y];
c[x][1] = y;
pushup(x);
y = x; x = fa[x];
}
}
我們要更換實虛邊了,於是就要將原來的實邊上的值給異或進去,將原來虛邊上的要變成實邊的值給亦或出來,然後pushup給結點繼承對應的關係式。
1
4 8
1 1 2
3 2 3
1 2 3
3 1 2
5
2 1 2
4 1 2
5
ans:
YES
YES
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
inline pair<int, int> PP(int x, int y) { return make_pair(min(x, y), max(x, y)); }
map<pair<int, int>, ll> mp;
int N, Q, c[maxN][2], r[maxN], fa[maxN], num[2], zero_num[2];
ll s[maxN], other[maxN], w[maxN], size[maxN];
inline bool isroot(int x) { return c[fa[x]][0] != x && c[fa[x]][1] != x; }
inline void pushup(int x)
{
if(!x) return;
s[x] = s[c[x][0]] ^ s[c[x][1]] ^ w[x] ^ other[x];
}
inline void pushr(int x)
{
if(!x) return;
swap(c[x][0], c[x][1]);
r[x] ^= 1;
}
inline void pushdown(int x)
{
if(!x) return;
if(r[x])
{
pushr(c[x][0]);
pushr(c[x][1]);
r[x] = 0;
}
}
void Rotate(int x)
{
int y = fa[x], z = fa[y], k = c[y][1] == x, cop = c[x][k ^ 1];
if(!isroot(y)) c[z][c[z][1] == y] = x;
fa[x] = z;
c[y][k] = cop;
fa[cop] = y;
c[x][k ^ 1] = y;
fa[y] = x;
pushup(y); pushup(x);
}
int st[maxN];
void Splay(int x)
{
int y = x, z = 0;
st[++z] = y;
while(!isroot(y)) st[++z] = y = fa[y];
while(z) pushdown(st[z--]);
while(!isroot(x))
{
y = fa[x]; z = fa[y];
if(!isroot(y)) (c[z][0] == y) ^ (c[y][0] == x) ? Rotate(x) : Rotate(y);
Rotate(x);
}
}
void access(int x)
{
int y = 0;
while(x)
{
Splay(x);
other[x] ^= s[c[x][1]];
other[x] ^= s[y];
c[x][1] = y;
pushup(x);
y = x; x = fa[x];
}
}
void makeroot(int x)
{
access(x); Splay(x);
pushr(x);
}
int findroot(int x)
{
access(x); Splay(x);
while(c[x][0]) { pushdown(x); x = c[x][0]; }
Splay(x);
return x;
}
void split(int x, int y)
{
makeroot(x);
access(y); Splay(y);
}
void link(int x, int y)
{
makeroot(x);
if(findroot(y) != x)
{
other[y] ^= s[x];
fa[x] = y;
int old = 0, now = 0;
if(!s[y]) old++;
if(!s[x]) old++;
pushup(y);
if(!s[y]) now++;
zero_num[0] = zero_num[0] - old + now;
}
}
void cut(int x, int y)
{
makeroot(x);
int old = 0, now = 0;
if(!s[x]) old++;
access(y);
Splay(y);
fa[x] = c[y][0] = 0;
pushup(y);
if(!s[x]) now++;
if(!s[y]) now++;
zero_num[0] = zero_num[0] - old + now;
}
inline void init()
{
num[0] = num[1] = zero_num[0] = zero_num[1] = N; mp.clear();
for(int i=1; i<=N; i++)
{
r[i] = 0; c[i][0] = c[i][1] = 0; w[i] = s[i] = other[i] = 0;
size[i] = 1;
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &Q);
init();
int op, u, v, old, now; ll val;
while(Q--)
{
scanf("%d", &op);
if(op == 1)
{
scanf("%d%d", &u, &v);
link(u, v); num[0] --;
}
else if(op == 2)
{
scanf("%d%d", &u, &v);
cut(u, v); num[0] ++;
}
else if(op == 3)
{
old = now = 0;
scanf("%d%d", &u, &v);
val = rand() + 1LL; num[1] --;
mp[PP(u, v)] = val;
makeroot(u);
if(!s[u]) old++;
w[u] ^= val; pushup(u);
if(!s[u]) now++;
makeroot(v);
if(!s[v]) old++;
w[v] ^= val; pushup(v);
if(!s[v]) now++;
zero_num[0] = zero_num[0] - old + now;
}
else if(op == 4)
{
old = now = 0;
scanf("%d%d", &u, &v);
val = mp[PP(u, v)];
makeroot(u);
if(!s[u]) old++;
w[u] ^= val; pushup(u);
if(!s[u]) now++;
makeroot(v);
if(!s[v]) old++;
w[v] ^= val; pushup(v);
if(!s[v]) now++;
num[1] ++;
zero_num[0] = zero_num[0] - old + now;
}
else
{
if(num[0] == num[1] && zero_num[0] == num[0]) printf("YES\n");
else printf("NO\n");
}
}
}
return 0;
}