LGP6018 [Ynoi 2010] Fusion Tree 学习笔记

LGP6018 [Ynoi 2010] Fusion Tree 学习笔记

Luogu Link

前言

魔法森林里有一颗大树,下面经常有小孩召开法。

麦杰斯住在大树顶端,有一天他想改造一下大树,方便他巨大多喝水之后可以垃圾分类矿泉水瓶。

关于Ynoi为什么掺进了龚诗等迫真中华例区要素,我暂且蒙在古里。

题意简述

给定一棵 \(n\) 个结点的无根树。每个结点有个点权 \(a_i\)。需要支持三种操作:

  1. \(u\) 的所有邻接点的点权加 \(1\)
  2. \(a_u\) 减去 \(v\)
  3. 询问 \(u\) 的所有邻接点的点权的异或和。

\(n,m,a_i\le 5\times 10^5\)

做法解析

根存最低位的01Trie的套路题。

首先我们不会做无根树,所以我们设置一个根,这样“维护邻接点”就转化为了“维护父亲”和“维护儿子们”。

然后查异或和想到01Trie,看到范围加 \(1\) 想到根存最低位的那种01Trie。

那么这道题就很简单了,我们对每个结点维护一棵01Trie,里面存放自己的所有儿子的数值。对于 \(u\) 临接点加的操作,我们先找到 \(u\) 的父亲暴力删掉 \(u\) 原数值,更新数值后再加回来;然后对于 \(u\) 的那棵01Trie,我们来考察全局加一会发生什么。我们发现,原来最低位为 \(0\) 的现在最低位会变成 \(1\),反之亦然,所以我们把Trie的两个儿子对调;我们又发现对于那些进了位的数,如果其原来第二位是 \(1\),那还会再进一位,所以我们就从根不断重复这个过程:交换两个儿子后,沿着当前的 \(0\) 儿子方向递归这个过程。

单点减显然,也是暴力改。邻接异或和呢?我们维护01Trie上每个结点被经过的次数,如果某位出现奇数次这位就取到 \(1\)。这也是很好动态维护的。最后再异或上自己父亲的值就OK。

然后这题就做完了。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=5e5+5,MaxVb=18;
int N,M,X,Y,Opt,ans,A[MaxN];
vector<int> Tr[MaxN];
void addudge(int u,int v){
    Tr[u].push_back(v);
    Tr[v].push_back(u);
}
int tfa[MaxN],tag[MaxN];
void dfs(int u,int f){tfa[u]=f;for(int v : Tr[u])if(v!=f)dfs(v,u);}
struct BilexiTree{
    int rt[MaxN],ch[MaxN<<5][2],w[MaxN<<5],xorv[MaxN<<5],ncnt;
    void maintain(int u){
        w[u]=xorv[u]=0;
        if(ch[u][0])w[u]+=w[ch[u][0]],xorv[u]^=(xorv[ch[u][0]]<<1);
        if(ch[u][1])w[u]+=w[ch[u][1]],xorv[u]^=(xorv[ch[u][1]]<<1)|(w[ch[u][1]]&1);
    }
    void newnode(int &u){u=++ncnt;}
    void inse(int &u,int x,int d){
        if(!u)newnode(u);if(d>MaxVb){w[u]++;return;}
        inse(ch[u][x&1],x>>1,d+1);maintain(u);
    }
    void init(int n){
        for(int i=1;i<=n;i++)if(tfa[i])inse(rt[tfa[i]],A[i],0);
    }
    void dele(int u,int x,int d){
        if(d>MaxVb){w[u]--;return;}
        dele(ch[u][x&1],x>>1,d+1),maintain(u);
    }
    void swapit(int u){
        swap(ch[u][0],ch[u][1]);
        if(ch[u][0])swapit(ch[u][0]);
        maintain(u);
    }
    int aget(int u){return A[u]+tag[tfa[u]];}
    void rincr(int u){
        tag[u]++;swapit(rt[u]);int &f=tfa[u];if(!f)return;
        int &g=tfa[f];
        if(g)dele(rt[g],aget(f),0);
        A[f]++;
        if(g)inse(rt[g],aget(f),0);
    }
    void sdecr(int u,int x){
        int &f=tfa[u];
        if(f)dele(rt[f],aget(u),0);
        A[u]-=x;
        if(f)inse(rt[f],aget(u),0);
    }
    int getans(int u){return xorv[rt[u]]^aget(tfa[u]);}
}BlT;
int main(){
    readis(N,M);
    for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
    dfs(1,0);for(int i=1;i<=N;i++)readi(A[i]);
    BlT.init(N);for(int i=1;i<=M;i++){
        readis(Opt,X);
        if(Opt==1)BlT.rincr(X);
        if(Opt==2)readi(Y),BlT.sdecr(X,Y);
        if(Opt==3)ans=BlT.getans(X),writil(ans);
    }
    return 0;
}
posted @ 2025-05-14 15:12  矞龙OrinLoong  阅读(14)  评论(0)    收藏  举报