http://acm.hdu.edu.cn/showproblem.php?pid=5929
题目大意:
模拟一个栈,有四种操作:
PUSH x:往栈里压入x
POP:丢掉栈顶元素
REVERSE:把栈翻转过来
QUERY:从栈顶到栈底求nand(与非)
其中0 nand 0=1,1 nand 0=1,0 nand 1=1,1 nand 1=0.
输出每次的query结果,如果栈是空的输出Invalid.
题目分析:
栈长度最大为20w,又是双端的,所以用40w长的数组和两个指针模拟很容易实现前三个操作。
关键就是第四个。一开始不小心看成了异或,结果wa了一发,然后又想的是用线段树维护区间的nand值,然后写了几个数发现这个运算不满足结合律,也就是栈顶和栈底反过来的值都是不一样的~~
看了题解明白了,本题抓住的是任何东西nand 0都为1这一个性质,所以只需要用一个TreeSet(C++里就是set)来维护最左边和最右边的0就可以了,每次查询的时候,先读出离栈顶最远的那个0,然后看看0顶上有没有东西,如果有东西,不管是什么,这一段值都为1,如果没有就为0,那么就剩下了1串(因为前面一团东西nand 0就是1)求nand,显然只跟1的奇偶有关系。这里还有个坑导致wa了一发,就是0顶上没东西的时候,就只需要看0下面1的个数,而不是+1。
然后还有一个问题就是那个set的end()返回的迭代器是最大值后面的那个。。。。这块又wa了一发。
//source:2016CCPC东北地区大学生程序设计竞赛 - 重现赛
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IN(s) freopen(s,"r",stdin)
#define OUT(s) freopen(s,"w",stdout)
int t,n,x,ans,cnt;
int q[400010];
char op[10];
set<int> s;
int main() {
//IN("5929.in");
scanf("%d",&t);
int ca=1;
while(t--) {
printf("Case #%d:\n", ca++);
scanf("%d",&n);
int l=200005,r=200006;//双端对列从中点开始
s.clear();
bool d=0;//0 for left,1 for right
while(n--) {
scanf("%s",op);
if(op[0]=='P' && op[1]=='U') { //PUSH x
scanf("%d",&x);
if(d==0) {
q[l]=x;
if(x==0)
s.insert(l);
l--;
}
else {
q[r]=x;
if(x==0)
s.insert(r);
r++;
}
}
else if(op[0]=='P' && op[1]=='O') { //POP
if(d==0) {
if(q[l+1]==0)
s.erase(l+1);
l++;
}
else {
if(q[r-1]==0)
s.erase(r-1);
r--;
}
}
else if(op[0]=='Q') { //QUERY
if(r-l<=1)
printf("Invalid.\n");
else {
if(s.empty())
printf("%d\n", (r-l+1)&1);
else {
set<int>::iterator i = s.end();
i--;
//printf("Debug: begin=%d,end=%d,l=%d,r=%d,d=%d\n",*s.begin(),*i,l,r,d);
if(d==0) {
if(*i-l>1)
printf("%d\n",(r-*i)&1);
else
printf("%d\n", (r-*i+1)&1);
}
else {
if(r-*s.begin()>1)
printf("%d\n",(*s.begin()-l)&1);
else
printf("%d\n",(*s.begin()-1-l)&1);
}
}
}
}
else //REVERSE
d=!d;
}
}
}