题目
让你模拟栈,实现n(2<=n<=2e5)个操作,操作分四种
①PUSH 0/1 会将0或1放到栈顶
②POP 弹出栈顶元素,保证不会对空栈进行该操作
③REVERSE 对栈内元素顺序反转,12345变54321
④QUERY 询问从栈顶到栈底的与非值,如a1与非a2与非a3
(与非操作 0与非0==1 0与非1==1 1与非0==1 1与非1==0)
思路来源
https://blog.csdn.net/STILLxjy/article/details/53997549
题解
注意到前面不管是0还是1,与非后面的一个0就会变成1,所以我们需要记录最后一个0的位置
而栈会反转,所以变成模拟双端队列,没反转向后加,反转了向前加,这就需要记录第一个0的位置
而0是可以被pop掉的,所以不能只用两个下标当指针,得记录所有0的位置,
事实上,出现最后一个0后,后面只有两种情况,空或全是1①
而出现最后一个0前面也有两种情况,空或有元素②
然后也要考虑,没有最后一个0(即全是1)的情形③
0的位置比较重要,得分开记录,而连续的1顺序与非和逆序与非是一样的,所以只需记录数量,合并到一个值上即可
在代码中,分当前双端队列里有元素数为0,为1,为2,或大于2讨论较为方便
心得
心态炸了,就是这题让我前天训练赛爆零,谁让我一上来开大模拟
然后敲了两天才敲出来,WA了得有将近10发
主要一直不知道自己的代码有什么问题,
最后急中生智写了个弱化的数据生成.cpp一直随一直随强行对拍,
发现自己是读错题了……是从栈顶与非到栈底wtm
同时训练的时候爆零给我们的启示是,
大模拟写的不熟的话就疯狂if-else,管它什么精简美观……
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=4e5+50;
int t;
int l,r,a[maxn],emp,v;
int n;
char s[35];
int op;//开始是r 正向
int TOP(int v)
{
if(v)return r;
else return l;
}
int PUSH(int v)
{
if(v)return ++r;
else return --l;
}
int POP(int v)
{
if(v)return --r;
else return ++l;
}
int SIZE()
{
if(l==r)return !emp;
else return r-l+1;
}
int main()
{
//freopen("1.txt","r",stdin);
//freopen("WA.txt","w",stdout);
scanf("%d",&t);
for(int cas=1;cas<=t;++cas)
{
memset(a,-1,sizeof a);
l=r=2e5+5;op=1;emp=1;
scanf("%d",&n);
printf("Case #%d:\n",cas);
for(int i=1;i<=n;++i)
{
scanf("%s",s);
//注意答案是从栈顶与非到栈底 看临近栈底的0 WA 4h
if(s[0]=='Q')
{
int sz=SIZE();
if(!sz)puts("Invalid.");
else if(sz==1)
{
if(a[TOP(!op)]==0)puts("0");
else printf("%d\n",a[TOP(!op)]%2);
}
else if(sz==2)
{
if(a[TOP(!op)]==0)puts("1");
else printf("%d\n",a[TOP(!op)]%2);
}
else
{
if(a[TOP(!op)]==0)puts("1");
else printf("%d\n",(a[TOP(!op)]+1)%2);//多了一个可以变1的0
}
}
else if(s[0]=='P')
{
if(s[1]=='U')
{
scanf("%d",&v);
if(SIZE()==0)
{
a[TOP(op)]=v;
emp=0;
}
else if(a[TOP(op)]>=1&&v==1)a[TOP(op)]++;//连着的一压在一起
else a[PUSH(op)]=v;
}
else if(s[1]=='O')
{
if(SIZE()==0)continue;
if(a[TOP(op)]>1)a[TOP(op)]--;
else if(SIZE()==1)emp=1;//只有0或1
else POP(op);
}
}
else if(s[0]=='R')op=!op;
}
}
return 0;
}
生成数据
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
int n,sz;
void solve(int x)
{
if(x<=4)
{
printf("PUSH %d\n",rand()%2);
sz++;
}
else if(x==5)printf("REVERSE\n");
else if(x==6)
{
if(sz==0)solve(rand()%5);
else
{
printf("POP\n");
sz--;
}
}
else if(x==7)printf("QUERY\n");
}
int main()
{
freopen("1.txt","w",stdout);
printf("%d\n",1);
n=20;
printf("%d\n",n);
for(int i=1;i<=n;++i)
solve(rand()%8);
}