P7073题解
这题比赛中我只拿30,但是离正解非常接近惹,稍微修改就AC。
经我研究这题是氵题哦~(难度不到黄题,但比橙题难上那么一点点。)
后缀表达式改如何处理呢?
1.遇到x,就往后搜索,把x后面的数字记录下来,并把相应的值(1/0)加入一个数组(定义为num),相应的下标加入s1,该数在s1里的位置加入pla,并且把它在s1里的位置入栈;
2.遇到!,取出栈头,把其num取反得到的值加入num,把!加入s1(我定义“!”是1000001,“&”是1000002,“|”是1000003),把其fa改为!在s1 里的位置,再把!在s1里位置入栈。
3.遇到“&”或“|”,取出两个栈头,把相应经过与或者或运算得到的结果加入num,把运算符加入s1,再把两个栈头互相加入到对方的br里,把两个栈头的fa改为&或|在s1里的位置,把运算符的位置加入栈。
4.根据后缀表达式的定义,最后一个字符必定是父节点,即num[s1[0]]就是我们要的答案。
5.为什么遇到运算符,栈头一定就是它的儿子呢?因为如果不先用排在后面的,而选了前面的,那么就一定会有num没有被计算。可以证明,如果后缀表达式无误,则取栈头一定会被计算完。(其实也是后缀表达式的性质awa)
这是处理的代码:
for(int i=0;i<s.length();i++)
{
if(s[i]=='x')
{
s1[0]++;
while(++i && s[i]>='0' && s[i]<='9') s1[s1[0]]=s1[s1[0]]*10+s[i]-'0';
pla[s1[s1[0]]]=s1[0];
num[s1[0]]=a[s1[s1[0]]];
fatherless.push(s1[0]);
}
else
{
if(s[i]=='!')
{
num1=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000001;
num[s1[0]]=!num[num1];
fatherless.push(s1[0]);
fa[num1]=s1[0];
}
if(s[i]=='&')
{
num1=fatherless.top();
fatherless.pop();
num2=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000002;
if(num[num1]==1 && num[num2]==1) num[s1[0]]=1;
else num[s1[0]]=0;
fatherless.push(s1[0]);
fa[num1]=s1[0];
fa[num2]=s1[0];
br[num1]=num2;
br[num2]=num1;
}
if(s[i]=='|')
{
num1=fatherless.top();
fatherless.pop();
num2=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000003;
if(num[num1]==0 && num[num2]==0) num[s1[0]]=0;
else num[s1[0]]=1;
fatherless.push(s1[0]);
fa[num1]=s1[0];
fa[num2]=s1[0];
br[num1]=num2;
br[num2]=num1;
}
}
}
接着,对于每一个要取反的数,找到它的fa(显然肯定是一个运算符),如果该数的num取反后对结果没有影响,就直接输出num[s1[0]],(考试的时候我就忘了这一点,导致TLE,不过加入后稳稳地AC)否则继续dfs。
这是dfs代码:
void f(int k)
{
if(k==s1[0])
{
printf("%d\n",num[k]);
return;
}
if(s1[fa[k]]==1000001)
{
bool nup=num[fa[k]];
if(nup==!num[k])
{
printf("%d\n",num[s1[0]]);
return;
}
num[fa[k]]=!num[k];
f(fa[k]);
num[fa[k]]=nup;
return;
}
if(s1[fa[k]]==1000002)
{
bool nup=num[fa[k]];
if(num[br[k]]==1 && num[k]==1) num[fa[k]]=1;
else num[fa[k]]=0;
if(nup==num[fa[k]])
{
printf("%d\n",num[s1[0]]);
return;
}
f(fa[k]);
num[fa[k]]=nup;
return;
}
if(s1[fa[k]]==1000003)
{
bool nup=num[fa[k]];
if(num[br[k]]==0 && num[k]==0) num[fa[k]]=0;
else num[fa[k]]=1;
if(nup==num[fa[k]])
{
printf("%d\n",num[s1[0]]);
return;
}
f(fa[k]);
num[fa[k]]=nup;
return;
}
}
这就是大体的思路,送上完整代码(当中的read()函数是快读,不过并没有什么用武之地,因为该TLE的都TLE了):
#include<bits/stdc++.h>
using namespace std;
string s;
int n,q,kk,num1,num2,s1[1000005],pla[100005],fa[1000005],br[1000005];
bool a[100005],num[1000005];
stack<int> fatherless;
void f(int k)
{
if(k==s1[0])
{
printf("%d\n",num[k]);
return;
}
if(s1[fa[k]]==1000001)
{
bool nup=num[fa[k]];
if(nup==!num[k])
{
printf("%d\n",num[s1[0]]);
return;
}
num[fa[k]]=!num[k];
f(fa[k]);
num[fa[k]]=nup;
return;
}
if(s1[fa[k]]==1000002)
{
bool nup=num[fa[k]];
if(num[br[k]]==1 && num[k]==1) num[fa[k]]=1;
else num[fa[k]]=0;
if(nup==num[fa[k]])
{
printf("%d\n",num[s1[0]]);
return;
}
f(fa[k]);
num[fa[k]]=nup;
return;
}
if(s1[fa[k]]==1000003)
{
bool nup=num[fa[k]];
if(num[br[k]]==0 && num[k]==0) num[fa[k]]=0;
else num[fa[k]]=1;
if(nup==num[fa[k]])
{
printf("%d\n",num[s1[0]]);
return;
}
f(fa[k]);
num[fa[k]]=nup;
return;
}
}
int read()
{
int res=0,f=1;
char C=getchar();
while(C<'0' || C>'9')
{
if(C=='-') f=-1;
C=getchar();
}
while(C>='0' && C<='9') res=res*10+C-'0',C=getchar();
return res*f;
}
int main()
{
//freopen("expr.in","r",stdin);
//freopen("expr.out","w",stdout);
getline(cin,s);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=0;i<s.length();i++)
{
if(s[i]=='x')
{
s1[0]++;
while(++i && s[i]>='0' && s[i]<='9') s1[s1[0]]=s1[s1[0]]*10+s[i]-'0';
pla[s1[s1[0]]]=s1[0];
num[s1[0]]=a[s1[s1[0]]];
fatherless.push(s1[0]);
}
else
{
if(s[i]=='!')
{
num1=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000001;
num[s1[0]]=!num[num1];
fatherless.push(s1[0]);
fa[num1]=s1[0];
}
if(s[i]=='&')
{
num1=fatherless.top();
fatherless.pop();
num2=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000002;
if(num[num1]==1 && num[num2]==1) num[s1[0]]=1;
else num[s1[0]]=0;
fatherless.push(s1[0]);
fa[num1]=s1[0];
fa[num2]=s1[0];
br[num1]=num2;
br[num2]=num1;
}
if(s[i]=='|')
{
num1=fatherless.top();
fatherless.pop();
num2=fatherless.top();
fatherless.pop();
s1[++s1[0]]=1000003;
if(num[num1]==0 && num[num2]==0) num[s1[0]]=0;
else num[s1[0]]=1;
fatherless.push(s1[0]);
fa[num1]=s1[0];
fa[num2]=s1[0];
br[num1]=num2;
br[num2]=num1;
}
}
}
q=read();
for(int i=1;i<=q;i++) kk=pla[read()],num[kk]=!num[kk],f(kk),num[kk]=!num[kk];
return 0;
}
这题主要看细心,因为我太粗惹,所以只拿惹30分。其实如果考试不吃巧克力而多花点时间在代码上,爷就320惹,可惜啊。。。