[TJOI2018]xor

题目大意:

有一棵树,根节点为1。每个点有点权。有两种操作。

1. 求节点x所在子树中点权与y异或的最大值。
2. 求x到y的路径上点权与z异或的最大值。

解题思路:

可持久化字典树。

对于第一种操作,我们对树进行dfs遍历,求出每个节点的dfs序(树剖),然后由于子树中dfs序连续,所以相当于区间的询问。对每个1~x区间建trie即可。

对于第二种操作,我们对每个节点建一颗trie,记录其到根的路径上的信息。

然后常规求LCA,减一减即可。

C++ Code:

#include<bits/stdc++.h>
const int N=1e5+5;
int ch[N<<6][2],siz[N<<6],ccnt=0,n,q,a[N],head[N],to[N<<1],nxt[N<<1];
int sz[N],dep[N],son[N],top[N],trie1[N],trie2[N],dfn[N],idfn[N],idx=0,fa[N],ans;
inline int readint(){
    int c=getchar(),d=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
    d=(d<<3)+(d<<1)+(c^'0');
    return d;
}
void Insert(int&nw,int&o,int&p,int pp=30){
    nw=++ccnt;
    if(!~pp)return(void)(siz[nw]=siz[o]+1);
    ch[nw][0]=ch[o][0];
    ch[nw][1]=ch[o][1];
    int nxt=(p>>pp)&1;
    Insert(ch[nw][nxt],ch[o][nxt],p,pp-1);
    siz[nw]=siz[ch[nw][0]]+siz[ch[nw][1]];
}
void dfs1(int now,int pre){
    sz[now]=1;
    Insert(trie1[now],trie1[pre],a[now]);
    for(int i=head[now];i;i=nxt[i])
    if(!dep[to[i]]){
        dep[to[i]]=dep[now]+1;
        fa[to[i]]=now;
        dfs1(to[i],now);
        sz[now]+=sz[to[i]];
        if(!son[now]||sz[to[i]]>sz[son[now]])son[now]=to[i];
    }
}
void dfs2(int now){
    idfn[dfn[now]=++idx]=now;
    if(son[now])top[son[now]]=top[now],dfs2(son[now]);
    for(int i=head[now];i;i=nxt[i])
    if(to[i]!=son[now]&&dep[to[i]]>dep[now])
    dfs2(top[to[i]]=to[i]);
}
inline int LCA(int x,int y){
    while(top[x]!=top[y])
    if(dep[top[x]]>=dep[top[y]])x=fa[top[x]];else
    y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}
void query(int&R,int&L,int&num,int pp=30){
    if(!~pp)return;
    int nw=(num>>pp)&1^1;
    if(siz[ch[R][nw]]>siz[ch[L][nw]]){
        ans|=1<<pp;
        query(ch[R][nw],ch[L][nw],num,pp-1);
    }else
    query(ch[R][!nw],ch[L][!nw],num,pp-1);
}
void query2(int&x,int&y,int&lca,int&fa,int&num,int pp=30){
    if(!~pp)return;
    int nw=(num>>pp)&1^1;
    if(siz[ch[x][nw]]+siz[ch[y][nw]]-siz[ch[lca][nw]]-siz[ch[fa][nw]]>0){
        ans|=1<<pp;
        query2(ch[x][nw],ch[y][nw],ch[lca][nw],ch[fa][nw],num,pp-1);
    }else
    query2(ch[x][!nw],ch[y][!nw],ch[lca][!nw],ch[fa][!nw],num,pp-1);
}
int main(){
    n=readint(),q=readint();
    for(int i=1;i<=n;++i)a[i]=readint();
    for(int i=1;i<n;++i){
        int u=readint(),v=readint();
        to[i<<1]=v;nxt[i<<1]=head[u];
        head[u]=i<<1;
        to[i<<1|1]=u;nxt[i<<1|1]=head[v];
        head[v]=i<<1|1;
    }
    memset(dep,0,sizeof dep);
    memset(son,0,sizeof son);
    dep[1]=1;
    dfs1(1,0);
    dfs2(1);
    for(int i=1;i<=n;++i)
    Insert(trie2[i],trie2[i-1],a[idfn[i]]);
    while(q--)
    if(readint()==1){
        int x=readint(),y=readint();
        ans=0;query(trie2[dfn[x]+sz[x]-1],trie2[dfn[x]-1],y);
        printf("%d\n",ans);
    }else{
        int x=readint(),y=readint(),z=readint();
        int lca=LCA(x,y);
        ans=0;
        query2(trie1[x],trie1[y],trie1[lca],trie1[fa[lca]],z);
        printf("%d\n",ans);
    }
    return 0;
}

 

  

 

转载于:https://www.cnblogs.com/Mrsrz/p/9605160.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值