区间异或的计算

区间异或

1.基础知识

​ 1.对于区间异或 我们可以 仿照 前缀和的思路 对于前缀和 我们假设 原始数组为 a [ N ] a[N] a[N] 那么前缀和 数组的公式可以写成
p r e t [ i ] = p r e t [ i − 1 ] + p r e t [ i ] pret[i]=pret[i-1]+pret[i] pret[i]=pret[i1]+pret[i]
​ 而对于比如 求解 区间 [l,r]的和 我们可以由以下的公式求解
a n s = p r e t [ j ] − p r e t [ i − 1 ] ; ans=pret[j]-pret[i-1]; ans=pret[j]pret[i1];
​ 对于异或运算 我们可以类比 一下前缀和的加法运算 用一个前缀和数组 来记录整个的 异或 和

​ 但是 异或和 有一个问题 就是我们还原的时候应该怎么还原

​ 对于这个问题 我们可以证明一下 请看以下的三个公式
a ⊕ b = c 两边同时异或上 b 等式仍然成立 a ⊕ b ⊕ b = c ⊕ b 异或满足结合律 a ⊕ ( b ⊕ b ) = c ⊕ b a\oplus b=c \\ 两边同时异或上b 等式仍然成立 \\ a\oplus b\oplus b=c\oplus b \\ 异或满足结合律 a\oplus (b\oplus b )=c \oplus b ab=c两边同时异或上b等式仍然成立abb=cb异或满足结合律a(bb)=cb
​ 这样我们就可以得到一个很神奇的结论
a ⊕ b = c a = b ⊕ c 其实异或运算最重要的性质就是自己和自己异或等于 0 以及异或满足结合律 我们做题的思路也是源于此 a \oplus b=c \quad a=b\oplus c \\ 其实异或运算最重要的性质就是自己和自己异或等于0 以及异或满足结合律\\ 我们做题 的思路也是源于此 ab=ca=bc其实异或运算最重要的性质就是自己和自己异或等于0以及异或满足结合律我们做题的思路也是源于此
​ 那么我们的 前缀和 取一个 [ l , r ] [l,r] [l,r]的 区间 我们就可 采用以下的公式

​ 然后 我们可以这么理解
p r e t [ r ] = a 1 ⊕ a 2 ⊕ a 3 ⊕ ⋯ ⊕ a r p r e t [ l − 1 ] = a 1 ⊕ a 2 ⋯ ⊕ a l − 1 我们要求解 a l ⊕ a l + 1 … a r 那么我们的 p r e t [ l − 1 ] ⊕ p r e t [ r ] 那么就是 a n s 我们完全可以看作他们两个异或是 a 1 和 a 1 结合等于 0 这样以此类推最后得到结果 所以异或的前缀和完美解决 pret[r]=a_1 \oplus a_2 \oplus a_3 \oplus \dots \oplus a_r \\ pret[l-1]=a_1 \oplus a_2 \dots \oplus a_{l-1} \\ 我们要求解 a_l\oplus a_{l+1} \dots a_r \\ 那么 我们的 pret[l-1] \oplus pret[r] 那么就是 ans\\ 我们完全可以看作 他们两个异或 是a_1和a_1结合等于0 这样以此类推 最后得到 结果 \\所以 异或的 前缀和完美解决 pret[r]=a1a2a3arpret[l1]=a1a2al1我们要求解alal+1ar那么我们的pret[l1]pret[r]那么就是ans我们完全可以看作他们两个异或是a1a1结合等于0这样以此类推最后得到结果所以异或的前缀和完美解决

2.例题

https://codeforces.com/contest/1872/problem/E

//变量ans代表所有为1 的数异或的结果 对于为0的数异或的结果 我们可以 让ans和整个区间做异或 因为 这样为1的会自己和自己异或
//对于改变操作 我们只需要 让ans和这个区间再做一次异或 这样相同的异或就会消掉 不同的 就会得到计算 
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+100;
int a[N];
long long int pret[N];
string s;
void solve()
{
    int n;
    cin>>n;
    memset(a,0,sizeof(a));
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }   
    cin>>s;
    long long int ans=0;
    for(int i=1;i<=s.length();i++)
    {
        if(s[i-1]=='1'){
            ans=ans^a[i];
        }
        pret[i]=pret[i-1]^a[i];


    }
    int q;
    cin>>q;
    while(q--)
    {
        int op;
        cin>>op;
        if(op==1){

            int l,r;
            cin>>l>>r;
            ans=ans^(pret[r]^pret[l-1]);


        }   
        else 
        {
            int x;
            cin>>x;
            if(x==1)
            cout<<ans<<" ";
            else 
            {
                int res=ans^pret[n]^pret[0];
                cout<<res<<" ";


            }


        }



    }
    




}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        solve();



    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值