问题 C: 与非
时间限制: 2 Sec 内存限制: 256 MB题目描述
作为一名新世纪共产主义的接班人,你认识到了资本主义的软弱性与妥协性,决定全面根除资本主义,跑步迈入共产主义。但是当你即将跨入共产主义大门的时候,遇到了万恶的资本家留下的与非电路封印,经过千辛万苦的研究,你终于把复杂的破解转变成了以下问题:
初始时你有一个空序列,之后有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中。
输出有多行。为对于每一个操作二的回答。
样例输入
61 11 11 02 1 22 1 32 2 3
样例输出
100
提示
【数据规模和约定】
数据点 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
考试时数据范围写错了,吓死人。。。正解是什么真值表加线段树,没看明白,但神犇lc考场上AC了,方法很神奇,而且,超第二的500多ms,啧。。挺难懂,但学明白了。
不扯了:设nand[i,j]是从i到j的与非和,f[i]表示nand[1,i].sum[i]=f[1]^f[2]^.....f[i]
那么sum就是f的前缀异或和。
那现在来证一个东西,数列中一旦出现零,nand的值一定变成同一个值(0&任何数等于0),那么找的数列有零后,nand[i,j]==f[j],这个时候前缀异或和就派上用场了。因为异或的逆运算也是异或sum[r]^sum[k-1]剩下的答案就出来了。
找的思路是:先暴力找,找到记录的cnt==f[i]时,跳出。。
最后,%lc orz orz orz
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
int sum=0,f=1;char x=getchar();
while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();}
return sum*f;
}
int n,tot=0,f[3900005],ans=0,sum[3900005],p=0,a[3900005];
int get(int l,int r)
{
int su=a[l],cnt=a[l],i;
for(i=l+1;i<=r;i++)
{
cnt=!(cnt&a[i]);
if(cnt==f[i])break;
su^=cnt;
}
if(i!=r+1)su^=sum[r]^sum[i-1];
return su;
}
int main()
{
n=read();int x,l,r;
while(n--)
{
x=read();
if(x==1)
{
l=read();l^=ans;
a[++tot]=l;
if(tot!=1)f[tot]=!(l&f[tot-1]),sum[tot]=sum[tot-1]^f[tot];
else f[tot]=l,sum[tot]=f[tot];
}
else
{
l=read();r=read();
if(ans==1){l=tot-l+1,r=tot-r+1,swap(l,r);}
if(l==r)ans=a[l];
else ans=get(l,r);
printf("%d\n",ans);
}
}
}