洛谷P4735 最大异或和

电脑从拯救者y7000p 2019换到了m1 air,不想在m1上再折腾一遍hexo,索性就用cnblog了...

信誓旦旦的学了可持久化Trie和01Trie来搞,结果出了一点问题,被卡了好久

先随便糊了一个暴力,用pre记一下异或前缀和,Xor表示异或总和

pre[i]^Xor就是 (i+1)~n的异或和

这么裸的一个暴力居然给了73分,11个点wa了3个点,

#include <bits/stdc++.h>
using namespace std;
int Xor=0;
int n,m;
int a[600010],pre[600010];
inline int read() {
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') f=(ch=='-')?-1:1,ch=getchar();
    while(ch<='9'&&ch>='0') x=(x<<3)+(x<<1)+(ch-'0'),ch=getchar();
    return x*f;
}
int main() {
    n=read(),m=read();
    for (int i=1;i<=n;i++) {
        cin>>a[i]; Xor^=a[i];
        pre[i]=pre[i-1]^a[i];
    }
    char op;
    int l,r,x;
    for (int i=1;i<=m;i++) {
        cin>>op;
        if(op=='A') {
            x=read();
            a[++n]=x;
            Xor^=x;
            pre[n]=pre[n-1]^x;
        } else {
            int Max=0;
            l=read(), r=read(), x=read();
            for (int i=l; i<=r; i++) {
                Max=max(Max,pre[i-1]^Xor^x);
            }
            cout<<Max<<endl;
        }
    }
    return 0;
}

然后考虑做一点简单的优化,最好是能够像最大异或数对那样维护一个01Trie,这样对于每个x都能速查

如果l=1,r=n,那么只要把全部的pre^Xor放到Trie里面,然后差x能取到的最大值

但是现在l,r是有变动的,所以我们要维护多版本的01Trie,只有一个端点还是比较简单的,但是两端都在动,怎么可持久化这问题就比较头疼

就是要找一个维护的方法,每次给出l,r,我们都能拿到由pre[l-1]^Xor pre[l]^Xor ... pre[r-1]^Xor构成的01Trie

或者说是拿到一个答案的时候,我们希望他在[l-1,r-1]的范围内

那么建一个可持久化Trie,设end[x]表示可持久化Trie的节点x是序列s中第几个二进制数末尾的节点

设latest[x]表示在可持久化Trie中以x为根的子树中end的最大值,从root[r-1]出发找答案的时候,只考虑latest值不小于l-1的节点

时间复杂度在O((N+M)log107)

数列里面插入一个0会比较好处理

#include <bits/stdc++.h>
using namespace std;
const int N=600010;
int trie[N*24][2], latest[N*24];
int s[N], Root[N], n, m, tot;
void insert(int i, int k, int p, int q) { // 统计子树latest,所以用到递归插入
// 当前为s[i]的第k位
    if (k<0) {
        latest[q]=i;
        return ;
    }
    int c=s[i]>>k&1;
    if (p)
        trie[q][c^1]=trie[p][c^1]; // 这里子节点只有c和c^1,反正c要重新插入,会被覆盖,所以就只继承c^1了
    trie[q][c]=++tot;
    insert(i, k-1, trie[p][c], trie[q][c]);
    latest[q]=max(latest[trie[q][0]], latest[trie[q][1]]);
}
int ask(int now, int val, int k, int limit) {
    if (k<0) return s[latest[now]] ^ val;
    int c = val >> k & 1;
    if (latest[trie[now][c^1]]>=limit)
        return ask(trie[now][c^1], val, k-1, limit);
    else
        return ask(trie[now][c], val, k-1, limit);
}
int main() {
    cin>>n>>m;
    latest[0]=-1;
    Root[0]=++tot;
    insert(0, 23, 0, Root[0]);
    for (int i=1; i<=n; i++) {
        int x; cin >> x;
        s[i]=s[i-1]^x;
        Root[i]=++tot;
        insert(i, 23, Root[i-1], Root[i]);
    }
    for (int i=1; i<=m; i++) {
        char op[2]; scanf("%s", op);
        if (op[0]=='A') {
            int x; scanf("%d", &x);
            Root[++n]=++tot;
            s[n]=s[n-1]^x;
            insert(n,23,Root[n-1],Root[n]);
        } else {
            int l, r, x; scanf("%d%d%d", &l, &r, &x);
            cout << ask(Root[r-1], x^s[n], 23, l-1) << endl;
        }
    }
    return 0;
}

也可以在每次加入节点的时候将trie的这条链的权值+1

查询的时候判断一个节点是否存在,如果sum[r]-sum[l-1]=0,那么这个值,也就是这条链不是在[l,r]里面出现的,就不考虑

#include<bits/stdc++.h>
using namespace std;
const int N=6e5+10;
int n,m,a[N],b[N],bin[30],Root[N];
struct Trie{
    int tot;
    int ch[N*24][2],sum[N*24];
    int insert(int x,int val){
        int tmp,y;tmp=y=++tot;
        for(int i=23;i>=0;i--)
        {
            ch[y][0]=ch[x][0]; ch[y][1]=ch[x][1];
            sum[y]=sum[x]+1;
            int t=val&bin[i];t>>=i;
            ch[y][t]=++tot;
            y=ch[y][t], x=ch[x][t];
        }
        sum[y]=sum[x]+1;
        return tmp;
    }
    int query(int l,int r,int val){
        int tmp=0;
        for(int i=23;i>=0;i--)
        {
            int t=val&bin[i];t>>=i;
            if(sum[ch[r][t^1]]-sum[ch[l][t^1]])
                tmp+=bin[i],r=ch[r][t^1],l=ch[l][t^1];
            else r=ch[r][t],l=ch[l][t];
        }
        return tmp;
    }
}trie;
int main()
{
    // cin.tie(nullptr)->sync_with_stdio(false);
    bin[0]=1;for(int i=1;i<30;i++) bin[i]=bin[i-1]<<1;
    cin>>n>>m;
    n++;
    for(int i=2;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) b[i]=b[i-1]^a[i];
    for(int i=1;i<=n;i++)
        Root[i]=trie.insert(Root[i-1],b[i]);
    char ch[5];
    int l,r,x;
    while(m--)
    {
        scanf("%s", ch);
        if(ch[0]=='A'){
            n++;
            cin>>a[n];b[n]=b[n-1]^a[n];
            Root[n]=trie.insert(Root[n-1],b[n]);
        }else{
            cin>>l>>r>>x;
            cout<<trie.query(Root[l-1],Root[r],b[n]^x)<<endl;
        }
    }
    return 0;
}

我傻乎乎的关流之后cin和scanf看了好久的问题没看出来,多花了快一个小时,草

posted @   FallacyMaker  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示