题目地址:https://www.luogu.com.cn/problem/P7073
第一次写想着直接用栈一步一步处理,但是只有五十分
这个题大致可以分为三步
1.输入的第一行数据数据处理为字符数组
2.输入的整数与数组里对应的数字对应上
3.根据输出需要改变的位置 处理表达式
50分写法,测试点会超时
#include<bits/stdc++.h>
using namespace std;
char a[1000005][10];
struct ST
{
//i 为在b数组的位置,v为其对应值 j为在a数组的位置
int i,v,j;
}st[1000005];
//两个栈,栈1用于存储数据,栈2用于运算数据
//zp用于临时存储
int z1[1000005],z2[1000005];
int zp[1000005];
int main()
{
int n;
//i 用于遍历输入的所有字符串
//j 用于遍历字符串a
int i=0,j=1;
char *fh=new char[10];
//将输入的第一行字符串处理
while(1)
{
//输入字符
char c=getchar();
//输入回车,结束循环
if(c=='\n')
{
//添加终止符
fh[i]=0;
i=0;
//字符串复制到a里
strcpy(a[j++],fh);
break;
}
//输入空格 则前面数据为一个完整单词
else if(c==' ')
{
//添加终止符
fh[i]=0;
i=0;
//字符串复制到a里
strcpy(a[j++],fh);
continue;
}
else
{
fh[i++]=c;
}
}
fh=nullptr;
cin>>n;
for(i=1;i<=n;i++)
{
int t;
cin>>t;
st[i].v=t;
st[i].i=i;
}
int x=1;
//数据全部进入z1栈 字符就以整数形式 处理
for(i=1;i<j;i++)
{
//将带x的 数据位置找到,运算处理
if(a[i][0]=='x')
{
int t=0;
for(int z=1;z<strlen(a[i]);z++)
{
t=10*t+a[i][z]-'0';
}
st[t].j=i;
z1[x++]=st[t].v;
}
else
{
z1[x++]=a[i][0];
}
}
cin>>n;
while(n--)
{
int k=1;
for(i=1;i<x;i++)
{
zp[i]=z1[i];
}
int t;
cin>>t;
zp[st[t].j]=!zp[st[t].j];
//运算处理
for(int i=1;i<j;i++)
{
if(zp[i]==1||zp[i]==0)
{
z2[k++]=zp[i];
}
else
{
if(zp[i]=='!')
{
z2[k-1]=!z2[k-1];
}
else if(zp[i]=='|')
{
if(z2[k-1]+z2[k-2]>=1)
{
z2[k-2]=1;
}
else
{
z2[k-2]=0;
}
k--;
}
else
{
if(z2[k-1]*z2[k-2]==1)
{
z2[k-2]=1;
}
else
{
z2[k-2]=0;
}
k--;
}
}
}
cout<<z2[1]<<endl;
memset(z2,0,x*4);
memset(zp,0,x*4);
}
}
思来想去想了好久,不知道怎么解决,想不到优化方法,看来一下题解,题解说用树存储,一下子豁然开朗,花了两个小时调整了一下代码,ac了。
树存储的要点:
1.叶子节点为数值,其余节点为符号
2.每个节点提前存储当前节点下所有运算出的值,运算时直接拿出来用
3.从修改的目标值往上依次修改,并判断val与nval是否相等(也就是有没有接着运算下去的必要,如果数值修改并不影响当前或者后续某一步的运算结果,则可以直接返回最终数值)
AC写法
#include<bits/stdc++.h>
using namespace std;
char a[1000005][10];
int j=1;
struct Tree{
//type 为1 表示数字,type为2 表示|,type为3 表示& type为4表示!
int type;
//整数存储对应的数值,字符存储其运算结果
int val,nval;
//左右节点和父节点
int l=0,r=0,p=0;
int i;
}tr1[1000005];
struct ST
{
//i 为在b数组的位置,v为其对应值 j为在a数组的位置
int i,v,j;
}st[1000005];
//tr2栈1用于临时存储数据,方便生成树
Tree tr2[1000005];
int sc_tr(Tree *tree,int k)
{
Tree &t=tree[k];
//如果只有一个节点 则直接返回
if(t.type==1&&t.p==0)
{
return t.nval;
}
else
{
//先运算,运算完成 子节点还原
if(t.type==4)
{
t.nval=!(tree[t.l].nval);
}
else if(t.type==2)
{
t.nval=(tree[t.l].nval)|(tree[t.r].nval);
}
else if(t.type==3)
{
t.nval=(tree[t.l].nval)&(tree[t.r].nval);
}
tree[t.r].nval=tree[t.r].val;
tree[t.l].nval=tree[t.l].val;
//加上判断 如果当前节点值没修改 则直接返回
if(t.val==t.nval)
{
return tree[j-1].nval;
}
// cout<<k<<" "<<t.type<<" "<<t.p<<" "<<t.val<<" "<<t.nval<<endl;
if(t.p!=0)
{
return sc_tr(tree,t.p);
}
}
return t.nval;
}
int main()
{
int n;
//i 用于遍历输入的所有字符串
//j 用于遍历字符串a
int i=0;
char *fh=new char[10];
//将输入的第一行字符串处理
while(1)
{
//输入字符
char c=getchar();
//输入回车,结束循环
if(c=='\n')
{
//添加终止符
fh[i]=0;
i=0;
//字符串复制到a里
strcpy(a[j++],fh);
break;
}
//输入空格 则前面数据为一个完整单词
else if(c==' ')
{
//添加终止符
fh[i]=0;
i=0;
//字符串复制到a里
strcpy(a[j++],fh);
continue;
}
else
{
fh[i++]=c;
}
}
cin>>n;
//输入的数据与值一一对应
for(i=1;i<=n;i++)
{
int t;
cin>>t;
st[i].v=t;
st[i].i=i;
}
int x=1;
//数据全部进入z1栈 字符就以整数形式 处理
for(i=1;i<j;i++)
{
//将带x的 数据位置找到,运算处理
if(a[i][0]=='x')
{
int t=0;
for(int z=1;z<strlen(a[i]);z++)
{
t=10*t+a[i][z]-'0';
}
tr1[x].val=st[t].v;
tr1[x].type=1;
tr1[x].i=i;
st[t].j=i;
x++;
tr2[i].val=st[t].v;
tr2[i].type=1;
tr2[i].i=i;
}
else
{
Tree tree;
tree.i=i;
if(a[i][0]=='!')
{
//节点赋值
tree.l=tr1[x-1].i;
tr2[tree.l].p=tree.i;
tree.type=4;
tree.val=!(tr2[tree.l].val);
//栈头更换
tr1[x-1]=tree;
}
else
{
//子节点处理
tree.l=tr1[x-2].i;
tree.r=tr1[x-1].i;
tr2[tree.l].p=tree.i;
tr2[tree.r].p=tree.i;
if(a[i][0]=='|')
{
tree.type=2;
tree.val=tr2[tree.l].val|tr2[tree.r].val;
}
else
{
tree.type=3;
tree.val=tr2[tree.l].val&tr2[tree.r].val;
}
//栈头更换
tr1[x-2]=tree;
x--;
}
//入树
tr2[i]=tree;
}
tr2[i].nval=tr2[i].val;
}
//变量n继续使用 避免变量名过多
cin>>n;
while(n--)
{
int t;
scanf("%d",&t);
//用另一个参数nval来代替更改值
tr2[st[t].j].nval=!(tr2[st[t].j].val);
int f=sc_tr(tr2, st[t].j);
printf("%d\n",f);
//数据还原
tr2[j-1].nval=tr2[j-1].val;
}
}