题意:
维护一个栈,支持以下操作:
从当前栈顶加入一个0或者1;
从当前栈顶弹掉一个数;
将栈顶指针和栈底指针交换;
询问a[top] nand a[top-1] nand ... nand a[bottom]的值。
nand是这样定义的:
?? 0 nand 0 = 1
?? 0 nand 1 = 1
?? 1 nand 0 = 1
?? 1 nand 1 = 0
关键是要发现性质,任何数nand 0,都会变成1。反复nand上1的话,则值会交替变化。
所以假设当前栈顶在左侧,只需要找到最右侧的0的位置,然后按照其右侧1的数量的奇偶性输出零或者一即可(如果最右侧的0在最左端,则其右侧有奇数个1就输出1,否则输出零。如果最右侧的零不在最左端,则其右侧有奇数个1就输出零,否则输出1)。
栈顶在右侧的情况同理。
用线段树维护。
#include<cstdio> #include<cstring> using namespace std; bool hav[400005<<2]; int a[400005]; int T,m,n,e[2]; void update(int p,int v,int rt,int l,int r){ if(l==r){ if(v==0){ hav[rt]=1; } else{ hav[rt]=0; } return; } int m=(l+r>>1); if(p<=m){ update(p,v,rt<<1,l,m); } else{ update(p,v,rt<<1|1,m+1,r); } hav[rt]=(hav[rt<<1] || hav[rt<<1|1]); } int find1(int rt=1,int l=1,int r=n){ if(l==r){ return l; } int m=(l+r>>1); if(hav[rt<<1]){ return find1(rt<<1,l,m); } else{ return find1(rt<<1|1,m+1,r); } } int find2(int rt=1,int l=1,int r=n){ if(l==r){ return l; } int m=(l+r>>1); if(hav[rt<<1|1]){ return find2(rt<<1|1,m+1,r); } else{ return find2(rt<<1,l,m); } } int main(){ //freopen("h.in","r",stdin); int x; bool dir=0; char op[10]; scanf("%d",&T); memset(a,-1,sizeof(a)); for(int zu=1;zu<=T;++zu){ printf("Case #%d:\n",zu); scanf("%d",&m); e[0]=m; e[1]=m+1; n=2*m; for(int i=1;i<=m;++i){ scanf("%s",op); if(op[2]==‘S‘){ scanf("%d",&x); if(!dir){ a[e[0]]=x; update(e[0],x,1,1,n); --e[0]; } else{ a[e[1]]=x; update(e[1],x,1,1,n); ++e[1]; } } else if(op[2]==‘P‘){ if(!dir){ ++e[0]; a[e[0]]=-1; update(e[0],-1,1,1,n); } else{ --e[1]; a[e[1]]=-1; update(e[1],-1,1,1,n); } } else if(op[2]==‘V‘){ dir^=1; } else{ if(e[0]==e[1]-1){ puts("Invalid."); continue; } if(hav[1]){ if(!dir){ int p=find2(); if(p==e[0]+1){ puts((e[1]-p-1)%2==1 ? "1" : "0"); } else{ puts((e[1]-p-1)%2==1 ? "0" : "1"); } } else{ int p=find1(); if(p==e[1]-1){ puts((p-e[0]-1)%2==1 ? "1" : "0"); } else{ puts((p-e[0]-1)%2==1 ? "0" : "1"); } } } else{ puts((e[1]-e[0]-1)%2==1 ? "1" : "0"); } } } memset(a,-1,sizeof(int)*(n+1)); memset(hav,0,sizeof(bool)*(n*4+1)); } return 0; }
时间: 2024-10-13 21:03:39