与非

题目大意

这里写图片描述

分类讨论

假若一个0左边有数,那么执行到0这里结果肯定为1。
只有左边没数的0和1与1与非会产生0,假若能统计得到的0的个数,便可以计算异或和。
连续一段1如果最左端有数,这一段有j个1,可以造成j/2上取整个0。
于是我们可以预处理前缀答案,还有一个位置往左最多扩展(是1即可扩展),有啥用呢?待会会讲。
l=1我们就可以直接输出了,必须判掉这种情况不然会有麻烦。
然后我们可以分类讨论:
如果第l位是0,那么就出现了一个0,而且因为是0,所以前面没有任何影响(即其不会存在于连续1中)。
l=r的话直接输出1。
l不等于r呢,0后面那段有多少个1我们需要知道,首先呢我们知道这一段的答案是可以用前缀和求出的,但这个不是对的,因为我们是把0当做了左边有数,即认为到了0这儿前缀与非就是1,但现实是0这儿前缀与非应该是0,所以应该减掉误的加上对的。
如果第l位是1,那么我们得到左边有j个1,l往右有k个1。
sum[r]=sum[l-1]-(j+1)/2+ans-k/2+(j+k+1)/2
ans=sum[r]-sum[l-1]+(j+1)/2+k/2-(j+k+1)/2
这样就是对的吗?不一定,如果往左一定延伸到了首部,那就出了问题,因为我们会认为前面有一个0让这一段1靠着,所造成的影响就是除以2上取整,但现在没有这个0,一开始就是1,就应该是除以2下取整。
只有这种情况会错吗?不一定,还有你虽然没延伸到头,但你延伸的位置所靠的0就是头,我们以为到0这里前缀与非是1,但这里实际是0。
于是这就是所有情况,求往左延伸直接使用left数组,求往右延伸可以二分然后利用left数组来判定。

最后吐槽一句,打这种方法好烧脑……

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=4000000+10;
int a[maxn],sum[maxn],num[maxn],left[maxn];
int i,j,k,l,r,t,n,m,top,ans;
bool czy;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int binary(int x,int y){
    int l=0,r=y,mid;
    while (l<r){
        mid=(l+r+1)/2;
        if (left[x+mid-1]>=mid) l=mid;else r=mid-1;
    }
    return l;
}
int main(){
    czy=1;
    n=read();
    while (n--){
        t=read();
        if (t==1){
            j=read();
            if (czy) j^=ans;
            a[++top]=j;
            num[top]=top==1?a[top]:!(num[top-1]&a[top]);
            sum[top]=sum[top-1]+(!num[top]);
            left[top]=(j?left[top-1]+1:0);
        }
        else{
            l=read();r=read();
            if (czy&&ans){
                l=top-l+1;
                r=top-r+1;
                swap(l,r);
            }
            if (l==1) ans=sum[r];
            else{
                if (!a[l]){
                    if (l==r) ans=1;
                    else{
                        j=binary(l+1,r-l);
                        ans=sum[r]-sum[l-1]-(j+1)/2+(j+2)/2;
                    }
                }
                else{
                    j=left[l-1];
                    k=binary(l,r-l+1);
                    ans=sum[r]-sum[l-1]+(j+1)/2+k/2-(j+k+1)/2;
                    if (j==l-1||j==l-2&&a[1]==0) ans=ans+(j+k+1)/2-(j+k)/2-(j+1)/2+j/2;
                }
            }
            ans=(r-l+1-ans)%2;
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值