P6018 [Ynoi2010] Fusion tree

lxl 说过邻域信息维护父亲一定死,所以数据结构维护每个点的儿子,特判父亲。

考虑每个点的数据结构需要支持什么操作:全局加一,单点修改,全局异或和。用 01Trie 维护。

与维护最大异或对的 01Trie 不同,从低位到高位建树,每个点上维护子树异或和,修改时 push up。

考虑怎么 push up。从 $u$ 往其 $0/1$ 子树中走到叶子形成的数,就是从 $u$ 的 $0/1$ 孩子走到叶子形成的数后面加一位 $0/1$,

所以 $u$ 子树数集包含 $u$ 的 $0$ 孩子子树数集中所有数后面加一位 $0$,$u$ 的 $1$ 孩子子树数集中所有数后面加一位 $1$,

即 $u$ 子树数集中,$u$ 的 $0$ 孩子子树异或和后面加一位 $0$;若 $u$ 的 $1$ 孩子子树数集大小为奇数,其异或和后面加一位 $1$,否则加 $0$。

考虑怎么全局加一。加一的过程实际上是从低到高按位取反,若当前位取反后为 $0$ 则继续取反下一位,

所以从上往下交换 $0/1$ 孩子,然后继续交换 $0$ 孩子的 $0/1$ 孩子。

指针非常好写。

#include <cstdio>
#include <algorithm>
#define V(x) a[x] + z[f[x]]
#define O(x, k) if(f[x]) C(V(x), 0, r[f[x]]);a[x] += k;if(f[x]) C(V(x), 0, r[f[x]])
using namespace std;
struct E{int v, t;}e[1000050];
int n, m, c, a[500050], z[500050], f[500050], h[500050];
void A(int u, int v) {e[++c] = {v, h[u]};h[u] = c;}
struct T
{
    T *c[2];int s, w;T() : c{0, 0}, w(0) {}void u()
    {
        if(c[s = w = 0]) s ^= c[0]->s, w ^= c[0]->w << 1;
        if(c[1]) s ^= c[1]->s, w ^= c[1]->w << 1 | c[1]->s; 
    }
}*r[500050];
void C(int x, int d, T *&p)
{
    if(!p) p = new T;if(d >= 20) return void(p->s ^= 1);
    C(x, d + 1, p->c[x >> d & 1]);p->u();
}
void M(T *p) {if(p) swap(p->c[0], p->c[1]), M(p->c[0]), p->u();}
void D(int u)
{
    for(int i = h[u], v;i;i = e[i].t)
        if(!r[v = e[i].v]) C(a[v], 0, r[f[v] = u]), D(v);
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1, u, v;i < n;++i)
        scanf("%d%d", &u, &v), A(u, v), A(v, u);
    for(int i = 1;i <= n;++i) scanf("%d", a + i);D(1);
    for(int i = 0, o, x, v;i < m;++i)
    {
        scanf("%d%d", &o, &x);
        switch(o)
        {
            case 1: ++z[x];M(r[x]);if(f[x]) {O(f[x], 1);}break;
            case 2: scanf("%d", &v);O(x, -v);break;
            case 3: printf("%d\n", (r[x] ? r[x]->w : 0) ^ V(f[x]));break;
        }
    }
    return 0;
}
posted @ 2023-05-04 15:29  5k_sync_closer  阅读(5)  评论(0编辑  收藏  举报  来源