J- Graph【2019ICPC瀋陽】【LCT+哈希】

題目鏈接


  有兩棵上限是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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章