2021牛客暑期多校训练营3 I-Kuriyama Mirai and Exclusive Or(异或+差分)

I-Kuriyama Mirai and Exclusive Or

KeHe题解
diabolusexnihil题解

不过diabolusexnihil大佬的题解有一部分写错了应该是:每次分裂标记 b l , i b_{l,i} bl,i需要给数组 [ l + 2 i − 1 , l + 2 i ) ⊕ 2 i − 1 [l+2^{i-1},l+2^i)\oplus2^{i-1} [l+2i1,l+2i)2i1然后标记分裂成 b l , i − 1 , b l + 2 i − 1 , i − 1 b_{l,i-1},b_{l+2^{i-1},i-1} bl,i1,bl+2i1,i1
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
    T res=0;T fg=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res*fg;
}
int n,q;
const int N=600010;
int a[N],tag[N];
bool b[N][22];
int main()
{
    n=rd(),q=rd();
    for(int i=1;i<=n;i++) a[i]=rd();
    while(q--)
    {
        int op=rd(),l=rd(),r=rd(),x=rd();
        if(!op) // 操作一直接差分打标记
        {
            tag[l]^=x,tag[r+1]^=x;
            continue;
        }
        // 考虑区间[l,l+2^i) 每次考虑lowbit(x) ^(x+i) -> ^x^i
        for(int i=0;i<=19;i++)
            if((x>>i&1)&&(l+(1<<i)-1)<=r)// 注意右端点
            {
                b[l][i]^=1;
                tag[l]^=(x>>i)<<i; 
                tag[l+(1<<i)]^=(x>>i)<<i;//差分打标记
                l+=(1<<i);
                x+=(1<<i);
            }
        // 最后一段区间[l,r]
        // 此时如果l+2^i<r 那么 x>>i&1一定是0 同样 ^(x+i) -> ^x^i效仿上面做法即可
        for(int i=19;i>=0;i--)
            if((l+(1<<i)-1)<=r)
            {
                b[l][i]^=1;
                tag[l]^=(x>>i)<<i;
                tag[l+(1<<i)]^=(x>>i)<<i;
                l+=(1<<i);
                x+=(1<<i);
            }
    }
    // 标记分裂
    for(int i=19;i>=1;i--)
        for(int j=1;j<=n;j++)
        {
            if(!b[j][i]) continue;
            b[j][i-1]^=1;// 分裂标记1
            if(j+(1<<(i-1))<=n)
            {
                b[j+(1<<(i-1))][i-1]^=1;// 分裂标记2
                // [l+2^{i-1},l+2^i) 需要打上抑或 2^{i-1}的标记
                tag[j+(1<<(i-1))]^=(1<<(i-1));
                if(j+(1<<i)<=n) tag[j+(1<<i)]^=(1<<(i-1));
            }
        }
    for(int i=1;i<=n;i++)
    {
        tag[i]^=tag[i-1];
        printf("%d%c",a[i]^tag[i]," \n"[i==n]);
    }
    
}

一辈子学不会的做法www

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值