【某集训题解】【DAY 2 T3】与非

题目描述

作为一名新世纪共产主义的接班人,你认识到了资本主义的软弱性与妥协性,决定全面根除资本主义,跑步迈入共产主义。但是当你即将跨入共产主义大门的时候,遇到了万恶的资本家留下的与非电路封印,经过千辛万苦的研究,你终于把复杂的破解转变成了以下问题:

初始时你有一个空序列,之后有N个操作。

操作分为一下两种:

1 x:在序列末尾插入一个元素x(x=0或1)。

2 L R:定义nand[L,R]为序列第L个元素到第R个元素的与非和,询问nand[L,L]^nand[L,L+1]^nand[L,L+2]^......^nand[L,R]。

Nand就是先与,再取反

输入

从文件nand.in中读入数据。

输入第一行一个正整数N,表示操作个数。

接下来N行表示N个操作。

为了体现程序的在线性,记lastans为上一次操作二的回答,初始lastans=0,。对于操作1,你需要对x异或lastans。对于操作二,设现在序列中的元素个数为M,如果lastans=1,那么你需要作如下操作:L=M-L+1,R=M-R+1,swap(L,R)

输出

输出到nand.out中。

输出有多行。为对于每一个操作二的回答。

样例输入

6
1 1
1 1
1 0
2 1 2
2 1 3
2 2 3

样例输出

1
0
0

提示

【数据规模和约定】

数据点 N的规模 操作一的个数M1 操作二的个数M2

1 N<=1000 M1<=500 M2<=500

2 N<=1000 M1<=500 M2<=500

3 N<=200000 M1<=100000 M2<=100000

4 N<=200000 M1<=100000 M2<=100000

5 N<=1000000 M1<=900000 M2<=100000

题解:

  据说正解是什么线段树(?)反正我跑得快233。

  其实不是很难,推一下式子就好了。

       Nand[ i ,j ]定义同题面。那么nand[i,j]=!(nand[ I , j-1 ]&a[ j ]) =。。。(!a[ I ]& a [ i+1])&…….

       F[ I ]表示nand[ 1,I ]。F[ j ]= ……!((!  F[i-1]&a [i ])&a[ i+1 ])……Sum[ i] =f[1]^…^f[I ]观察上下两式。F[j]与nand[ I ,J ]结果与!(a[ I ]& a [ i+1])和!((! F[i-1]&a [i ])&a[ i+1 ]))有关。分类讨论一下,当A[I+1 ]==0时,两式结果相等,当A[I+1 ]==1时,那么仅在(A[ I ]==1&&F[I-1]==1)||(A[I]==0)时对答案可能产生影响。前者使这一层变成0与1,后者使这一层变成1与0 。

(具体不太好写,我在下面写一下式子)

当A[ I ]==1&&F[I-1]==1/(A[I]==0时可以自己画一下)

假设到A[I+X]之前A[I+?]都是1

&A[I+1]

然后!

&A[I+2]

&A[I+3]

&A[I+4]

&A[I+5]

&A[I+X]

a[ I ]

0

1

0

1

0

1

! F[i-1]&a [i ]

1

0

1

0

1

1

这也就意味着在F[I+X]之前F[I+y]与nand[I,I+Y]一直是相反的,所以他们的区间异或和仅与X的奇偶性有关(大家可以自己推一下)。从I+X到R,F数组的结果一直与nand[I,I+X+…]相等,这也就意味着这之后的F异或和与nand相等,所以这部分我们可以用F数组的答案来代替nand。再考虑前面F与nand不相等的部分。已经说过这部分异或和仅与X的奇偶性有关。

       最终答案就是sum[R]^sum[L-1]^1(x为奇数)sum[R]^sum[L-1](x为偶数)。对于查单点要特判233。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=4000010;
inline int read(){
    int s=0,k=1;char ch=getchar();
    while(ch<'0'||ch>'9')   k=ch=='-'?-1:k,ch=getchar();
    while(ch>='0'&&ch<='9') s=s*10+(ch^48),ch=getchar();
    return s*k;
}
int n,m;
bool ans;
bool f[N],a[N];
bool sum[N];
int main(){
    n=read();
    for(int i=1,op,x,l,r;i<=n;i++){
        op=read();
        if(op==1){
            x=read()^ans;
            m++;
            a[m]=x;
            if(m==1)    f[m]=x;
            else    f[m]=1-(f[m-1]&x);
            sum[m]=sum[m-1]^f[m];
        } 
        else{
            l=read(),r=read();
            if(ans==1){
                l=m-l+1,r=m-r+1; swap(l,r);
            }
            ans=a[l];
            int j=l;
            bool s=1-(a[l]&a[l+1]);
            while(s!=f[j+1]&&j<r){
                ans^=s;
                j++;
                s=1-(s&a[j+1]);
            }
            if(j<r){
                ans^=sum[r]^sum[j];
            }
            printf("%d\n",ans);
        }
    }
}

【注】:其实在查询【L,R】第一个0出现位置应用二分查或者链表,但题目数据太水,就直接暴力咯。

链表版(不是自己打的,懒,而且实测居然比暴力慢233):

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int M=4000000+10;
int n,ans,cnt,p;
int pre[M],nxt[M],sum[M],f[M],val[M];
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int opt,l,r;
        opt=read();
        if(opt==1){
            //cout<<opt<<endl;
            val[++cnt]=read();
            val[cnt]^=ans;
            if(!val[cnt]){
                nxt[p]=cnt;
                p=cnt;
            }else pre[cnt]=p;
            if(cnt==1)
                f[1]=sum[1]=val[cnt];
            else{
                f[cnt]=!(f[cnt-1]&val[cnt]);
                sum[cnt]=sum[cnt-1]^f[cnt];
            }
        }else{
            //cout<<opt<<endl;
            l=read();r=read();
            if(ans==1){
                l=cnt-l+1;r=cnt-r+1;
                swap(l,r);
            }
            if(l==r)     { printf("%d\n",ans=val[l]);continue;}
            if(val[l]==f[l]) {printf("%d\n",ans=sum[r]^sum[l-1]);continue;}
            else{
                int pos=nxt[pre[l]];
                if(val[l]==0)       pos=nxt[l];
                pos=min(pos,r+1);
                int pd=pos-l;
                if(pd&1)  {printf("%d\n",ans=sum[r]^sum[l-1]^1);continue;}
                else      {printf("%d\n",ans=sum[r]^sum[l-1]);continue;}
            }
            //printf("%d\n",sum[r]^sum[l-1]);continue;
        }
    }
//    while(1);
    return 0;
}

 

转载于:https://www.cnblogs.com/Troywar/p/7308651.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值