题目链接
有两棵上限是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;
}