[BZOJ3282][tree][LCT]

50 篇文章 0 订阅
5 篇文章 0 订阅

[BZOJ3282][tree][LCT]

题目大意:

给定N个点以及每个点的权值,要你处理接下来的M个操作。操作有4种。操作从0到3编号。点从1到N编号。
0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是联通的。
1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接。
2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。
3:后接两个整数(x,y),代表将点X上的权值变成Y。

思路:

LCT裸题???

维护一下子树的xor和就好了。

代码:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 300005;
inline char get(void) {
    static char buf[100000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c;
    for (; !(c >= '0' && c <= '9'); c = get());
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}

int c[Maxn][2], fa[Maxn], rev[Maxn], s[Maxn], v[Maxn], st[Maxn], top;

inline void pushUp(int x) {
    int l = c[x][0], r = c[x][1];
    s[x] = s[l] ^ s[r] ^ v[x];
}
inline void pushDown(int x) {
    int &l = c[x][0], &r = c[x][1];
    if (rev[x]) {
        rev[l] ^= 1; rev[r] ^= 1; rev[x] ^= 1;
        swap(l, r);
    }
}
inline bool Rt(int x) {
    return c[fa[x]][0] != x && c[fa[x]][1] != x;
}
inline void rotate(int x) {
    int y = fa[x], z = fa[y], l = (c[y][1] == x), r = l ^ 1;
    if (!Rt(y)) {
        if (c[z][0] == y) c[z][0] = x;
        else c[z][1] = x;
    }
    fa[x] = z; fa[y] = x; fa[c[x][r]] = y;
    c[y][l] = c[x][r]; c[x][r] = y;
    pushUp(y), pushUp(x);
}

inline void splay(int x) {
    st[++top] = x;
    for (int i = x; !Rt(i); i = fa[i]) st[++top] = fa[i];
    while (top) pushDown(st[top--]);
    while (!Rt(x)) {
        int y = fa[x], z = fa[y];
        if (!Rt(y)) {
            if ((c[y][0] == x) ^ (c[z][0] == y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}

inline void access(int x) {
    for (int t = 0; x; t = x, x = fa[x])
        splay(x), c[x][1] = t, pushUp(x);
}
inline void makeroot(int x) {
    access(x), splay(x), rev[x] ^= 1;
}
inline void link(int x, int y) {
    makeroot(x), fa[x] = y;
}
inline void cut(int x, int y) {
    makeroot(x); access(y), splay(y);
    if (c[y][0] == x) c[y][0] = fa[x] = 0;
}
inline int find(int x) {
    access(x), splay(x);
    while (c[x][0]) x = c[x][0];
    return x;
}
int n, m;
int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    read(n), read(m);
    for (int i = 1; i <= n; i++) read(s[i]), v[i] = s[i];
    int op, x, y;
    for (int i = 1; i <= m; i++) {
        read(op), read(x), read(y);
        if (op == 0) {
            makeroot(x), access(y), splay(y);
            printf("%d\n", s[y]);
        } else if (op == 1) {
            if (find(x) != find(y)) link(x, y);
        } else if (op == 2) {
            if (find(x) == find(y)) cut(x, y);
        } else {
            access(x); splay(x); v[x]=y; pushUp(x);
        }
    }
    return 0;
}

完。

By g1n0st

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值