[UOJ#207]共价大爷游长沙

[UOJ#207]共价大爷游长沙

试题描述

火车司机出秦川,跳蚤国王下江南,共价大爷游长沙。每个周末,勤劳的共价大爷都会开车游历长沙市。

长沙市的交通线路可以抽象成为一个 \(n\) 个点 \(n-1\) 条边的无向图,点编号为 \(1\)\(n\),任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连。有一个包含一些点对 \((x,y)\)可重集合 \(S\),共价大爷的旅行路线是这样确定的:每次他会选择 \(S\) 中的某一对点 \((x,y)\),并从 \(x\) 出发沿着唯一路径到达 \(y\)

小L是共价大爷的脑残粉,为了见到共价大爷的尊容,小L决定守在这张图的某条边上等待共价大爷的到来。为了保证一定能见到他,显然小L必须选择共价大爷一定会经过的边——也就是所有共价大爷可能选择的路径都经过的边。

现在小L想知道,如果他守在某一条边,是否一定能见到共价大爷。

然而长沙市总是不断的施工,也就是说,可能某个时刻某条边会断开,同时这个时刻一定也有某条新边会出现,且任意时刻图都满足任意两点间均存在恰好一条路径的条件。注意断开的边有可能和加入的新边连接着相同的两个端点。共价大爷的兴趣也会不断变化,所以 \(S\) 也会不断加入新点对或者删除原有的点对。当然,小L也有可能在任何时候向你提出守在某一条边是否一定能见到共价大爷的问题。你能回答小L的所有问题吗?

输入

输入的第一行包含一个整数 \(\texttt{id}\),表示测试数据编号,如第一组数据的 \(\texttt{id}=1\),样例数据的 \(\texttt{id}\) 可以忽略。hack数据中的 \(\texttt{id}\) 必须为 \(0\)\(10\) 之间的整数。hack数据中 \(\texttt{id}\) 的值和数据类型没有任何关系。

输入的第二行包含两个整数 \(n,m\),分别表示图中的点数,以及接下来会发生的事件数,事件的定义下文中会有描述。初始时 \(S\) 为空。

接下来 \(n−1\) 行,每行两个正整数 \(x,y\),表示点 \(x\) 和点 \(y\) 之间有一条无向边。

接下来 \(m\) 行,每行描述一个事件,每行的第一个数 \(\texttt{type}\) 表示事件的类型。

\(\texttt{type}=1\),那么接下来有四个正整数 \(x,y,u,v\),表示先删除连接点 \(x\) 和点 \(y\) 的无向边,保证存在这样的无向边,然后加入一条连接点 \(u\) 和点 \(v\) 的无向边,保证操作后的图仍然满足题中所述条件。

\(\texttt{type}=2\),那么接下来有两个正整数 \(x,y\),表示在 \(S\) 中加入点对 \((x,y)\)

\(\texttt{type}=3\),那么接下来有一个正整数 \(x\),表示删除第 \(x\) 个加入 \(S\) 中的点对,即在第 \(x\)\(\texttt{type}=2\) 的事件中加入 \(S\) 中的点对,保证这个点对存在且仍然在 \(S\) 中。

\(\texttt{type}=4\),那么接下来有两个正整数 \(x,y\),表示小L询问守在连接点 \(x\) 和点 \(y\) 的边上是否一定能见到共价大爷,保证存在这样的无向边且此时 \(S\) 不为空。

输出

对于每个小L的询问,输出 YES 或者 NO(均不含引号)表示小L一定能或者不一定能见到共价大爷。

输入示例
0
5 7
1 2
1 3
2 4
1 5
2 1 5
1 1 5 2 5
4 2 5
2 1 4
4 2 5
3 1
4 2 4
输出示例
YES
NO
YES
数据规模及约定

对于 \(100\%\) 的数据,\(n \le 10^5\)\(m \le 3 \times 10^5\)

题解

原来随机权值再异或是靠谱的。。。

做法是这样的,我们需要一个方法能够快速实现查询某个集合是否为全集,我们不妨先想想 \(|S| \le 10\) 的情况。这样一个集合就可以用二进制表示成一个 \([0, 1023]\) 的整数,然后增一个元素或者去掉一个已有元素时就直接异或。那么现在我们可以沿用这个异或的办法,随机给每个新加的点对安排一个权值,出错率很低(具体多低不知道,实验证明可行。。。)

然后剩下的部分就是 LCT 咯。加入一个点对 \((u, v)\) 的时候,把 \(u\)\(v\) 路径上的全都异或上一个值,换边操作就是将被删边的权值异或到被删边两端点在新树上路径就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <ctime>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 500010
#define oo 2147483647
#define RND rand()

int n, all;

struct LCT {
    int fa[maxn], ch[maxn][2], val[maxn], Xor[maxn], tag[maxn], S[maxn], top;
    bool rev[maxn], siz[maxn];
    
    bool isrt(int u) { return ch[fa[u]][0] != u && ch[fa[u]][1] != u; }
    
    void maintain(int u) {
        Xor[u] = val[u]; siz[u] = u > n;
        if(ch[u][0]) Xor[u] ^= Xor[ch[u][0]], siz[u] ^= siz[ch[u][0]];
        if(ch[u][1]) Xor[u] ^= Xor[ch[u][1]], siz[u] ^= siz[ch[u][1]];
        Xor[u] ^= tag[u] * (siz[u] ^ (u > n));
        return ;
    }
    void pushdown(int u) {
        if(rev[u]) {
            if(ch[u][0]) rev[ch[u][0]] ^= 1;
            if(ch[u][1]) rev[ch[u][1]] ^= 1;
            swap(ch[u][0], ch[u][1]);
            rev[u] = 0;
        }
        if(tag[u]) {
            if(ch[u][0]) {
                tag[ch[u][0]] ^= tag[u];
                if(ch[u][0] > n) val[ch[u][0]] ^= tag[u];
                Xor[ch[u][0]] ^= tag[u] * siz[ch[u][0]];
            }
            if(ch[u][1]) {
                tag[ch[u][1]] ^= tag[u];
                if(ch[u][1] > n) val[ch[u][1]] ^= tag[u];
                Xor[ch[u][1]] ^= tag[u] * siz[ch[u][1]];
            }
            tag[u] = 0;
            maintain(u);
        }
        return ;
    }
    void rotate(int u) {
        int y = fa[u], z = fa[y], l = 0, r = 1;
        if(!isrt(y)) ch[z][ch[z][1]==y] = u;
        if(ch[y][1] == u) swap(l, r);
        fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
        ch[y][l] = ch[u][r]; ch[u][r] = y;
        return maintain(y);
    }
    void splay(int u) {
        int t = u;
        while(!isrt(t)) S[++top] = t, t = fa[t]; S[++top] = t;
        while(top) pushdown(S[top--]);
        while(!isrt(u)) {
            int y = fa[u], z = fa[y];
            if(!isrt(y)) {
                if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
                else rotate(y);
            }
            rotate(u);
        }
        return maintain(u);
    }
    
    void access(int u) {
        splay(u); ch[u][1] = 0; maintain(u);
        while(fa[u]) splay(fa[u]), ch[fa[u]][1] = u, maintain(fa[u]), splay(u);
        return ;
    }
    void makert(int u) {
        access(u); rev[u] ^= 1;
        return ;
    }
    void link(int a, int b) {
        makert(b); fa[b] = a;
        return ;
    }
    void cut(int a, int b) {
        makert(a); access(b); fa[a] = ch[b][0] = 0;
        return maintain(b);
    }
    void cover(int a, int b, int v) {
        makert(a); access(b); tag[b] ^= v; if(b > n) val[b] ^= v; Xor[b] ^= v * siz[b];
        return ;
    }
    int query(int a, int b) {
        makert(a); access(b);
        return Xor[b];
    }
} sol;

#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)

unordered_map <int, bool> has;
map <pii, int> eid;
struct Pair {
    int u, v, rnd;
    Pair() {}
    Pair(int _1, int _2, int _3): u(_1), v(_2), rnd(_3) {}
} ps[maxn];
int cntp, cnte;

int main() {
    srand((unsigned)time(0));
    read();
    n = read(); int q = read();
    rep(i, 1, n - 1) {
        int u = read(), v = read();
        if(u > v) swap(u, v);
        eid[mp(u,v)] = ++cnte;
        sol.link(u, cnte + n); sol.link(cnte + n, v);
    }
    rep(kase, 1, q) {
        int tp = read(), u, v;
        if(tp == 1) {
            u = read(); v = read(); if(u > v) swap(u, v);
            int id = eid[mp(u,v)], nu = read(), nv = read(), nid, Xor = sol.query(u, v);
            if(nu > nv) swap(nu, nv); nid = eid.count(mp(nu, nv)) ? eid[mp(nu,nv)] : (eid[mp(nu,nv)] = ++cnte);
            sol.cut(u, id + n); sol.cut(id + n, v);
            sol.val[nid+n] = sol.tag[nid+n] = sol.Xor[nid+n] = 0;
            sol.link(nu, nid + n); sol.link(nid + n, nv);
            sol.cover(u, v, Xor);
        }
        if(tp == 2) {
            u = read(); v = read();
            int rnd = RND;
            while(has.count(rnd)) rnd = RND;
            has[rnd] = 1;
            sol.cover(u, v, rnd);
            ps[++cntp] = Pair(u, v, rnd); all ^= rnd;
        }
        if(tp == 3) {
            u = read();
            sol.cover(ps[u].u, ps[u].v, ps[u].rnd); all ^= ps[u].rnd;
        }
        if(tp == 4) {
            u = read(); v = read();
            puts(sol.query(u, v) == all ? "YES" : "NO");
        }
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8027824.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值