#可持久化01trie,树链剖分#洛谷 4592 异或

题目

现在有一颗以1为根节点的由 n n n个节点组成的树,树上每个节点上都有一个权值 v i v_i vi​。现在有 Q Q Q次操作,操作如下:

1 1 1 x x x y y y:查询节点 x x x的子树中与 y y y异或结果的最大值
2 x x x y y y z z z:查询路径 x x x y y y上点与 z z z异或结果最大值


分析

这道题有两种操作,所以要分成两个可持久化01trie分别求解,其它其实有点类似最大异或和那道题,然而还要求LCA所以我就写了个树剖


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100101;
struct node{int y,next;}e[N<<1];
int ls[N],rt1[N],rt2[N],tot,k=1,n,q,top[N],dfn[N],son[N],dep[N],a[N],fat[N],big[N];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
struct Trie{
    int cnt[N<<6],trie[N<<6][2],tot;
    inline void Insert(int &rt,int x,int now){
        trie[++tot][0]=trie[rt][0],trie[tot][1]=trie[rt][1],cnt[tot]=cnt[rt]+1,rt=tot;
        if (~now) Insert(trie[rt][(x>>now)&1],x,now-1);
    }
    inline signed query(int rt,int hs,int x,int now){
        if (now==-1) return 0;
        rr int p=((x>>now)&1)^1;
        return cnt[trie[rt][p]]^cnt[trie[hs][p]]?query(trie[rt][p],trie[hs][p],x,now-1)|(1<<now):query(trie[rt][p^1],trie[hs][p^1],x,now-1); 
    }
}Tre;
inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
	for (rr int i=ls[x],mson=-1;i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		son[x]+=son[e[i].y];
		if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];
	}
}
inline void dfs2(int x,int linp){
	dfn[x]=++tot,top[x]=linp;
    Tre.Insert(rt1[tot]=rt1[tot-1],a[x],29);
    Tre.Insert(rt2[x]=rt2[fat[x]],a[x],29);
	if (!big[x]) return; dfs2(big[x],linp);
	for (rr int i=ls[x];i;i=e[i].next)
	if (e[i].y!=fat[x]&&e[i].y!=big[x])
	    dfs2(e[i].y,e[i].y);
}
inline signed lca(int x,int y){
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
		x=fat[top[x]];
	}
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
	return x;
}
inline signed max(int a,int b){return a>b?a:b;}
signed main(){
    n=iut(); q=iut(); Tre.tot=0;
    for (rr int i=1;i<=n;++i) a[i]=iut();
    for (rr int i=1;i<n;++i){
        rr int x=iut(),y=iut();
        e[++k]=(node){y,ls[x]},ls[x]=k,
        e[++k]=(node){x,ls[y]},ls[y]=k;
    }
    dfs1(1,0),dfs2(1,1);
    for (rr int i=1;i<=q;++i){
        rr int opt=iut(),x=iut(),y=iut();
        if (opt&1) print(Tre.query(rt1[dfn[x]+son[x]-1],rt1[dfn[x]-1],y,29));
            else{
                rr int z=iut(),Lca=lca(x,y);
                print(max(Tre.query(rt2[x],rt2[fat[Lca]],z,29),Tre.query(rt2[y],rt2[fat[Lca]],z,29)));
            }
        putchar(10);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值