当前位置: 首页 > news >正文

表达式(CSP-J 2021-Expr)题目详解

先把题目从洛谷搬上来

题目描述

小 C 热衷于学习数理逻辑。有一天,他发现了一种特别的逻辑表达式。在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为 0 或 1,运算从左往右进行。如果表达式中有括号,则先计算括号内的子表达式的值。特别的,这种表达式有且仅有以下几种运算:

  1. 与运算:a & b。当且仅当 a 和 b 的值都为 1 时,该表达式的值为 1。其余情况该表达式的值为 0。
  2. 或运算:a | b。当且仅当 a 和 b 的值都为 0 时,该表达式的值为 0。其余情况该表达式的值为 1。
  3. 取反运算:!a。当且仅当 a 的值为 0 时,该表达式的值为 1。其余情况该表达式的值为 0。

小 C 想知道,给定一个逻辑表达式和其中每一个操作数的初始取值后,再取反某一个操作数的值时,原表达式的值为多少。

为了化简对表达式的处理,我们有如下约定:

表达式将采用后缀表达式的方式输入。

后缀表达式的定义如下:

  1. 如果 E 是一个操作数,则 E 的后缀表达式是它本身。
  2. 如果 E 是 E1​ op E2​ 形式的表达式,其中op 是任何二元操作符,且优先级不高于 E1​ 、E2​ 中括号外的操作符,则 E 的后缀式为E1 E2 op,其中E1  E2 分别为E1​、E2​ 的后缀式。
  3. 如果 E 是 E1​ 形式的表达式,则 E1​ 的后缀式就是 E 的后缀式。

同时为了方便,输入中:

  1. 与运算符(&)、或运算符(|)、取反运算符(!)的左右均有一个空格,但表达式末尾没有空格
  2. 操作数由小写字母 x 与一个正整数拼接而成,正整数表示这个变量的下标。例如:x10,表示下标为 10的变量 x10​。数据保证每个变量在表达式中出现恰好一次

输入格式

第一行包含一个字符串 s,表示上文描述的表达式。
第二行包含一个正整数 n,表示表达式中变量的数量。表达式中变量的下标为 1,2,⋯,n。
第三行包含 n 个整数,第 i 个整数表示变量 xi​ 的初值。
第四行包含一个正整数 q,表示询问的个数。
接下来 q 行,每行一个正整数,表示需要取反的变量的下标。注意,每一个询问的修改都是临时的,即之前询问中的修改不会对后续的询问造成影响。
数据保证输入的表达式合法。变量的初值为 0 或 1。

输出格式

输出一共有 q 行,每行一个 0 或 1,表示该询问下表达式的值。

输入输出样例

输入 #1复制

x1 x2 & x3 |
3
1 0 1
3
1
2
3

输出 #1复制

1
1
0


这道题属实有点难度,考场时直接过了,回过头来发现还是有技巧性的

题解

对于数据的储存,我采用一下形式

首先定义一个结构体:

struct node{char op;int value;int dp[2];vector<node*>son;node(){dp[0]=0;dp[1]=0;value=0;op='F';                //F表示没有,可以标记叶节点vector<node*>son();   //初始化,不加也行}
};

dp数组下文会解释,value存储该节点子树的值,一个vector数组表示它的子节点,容易发现,子节点的数量取决于该节点的符号,为!时只有一个;而为&或|时则为两个。叶节点都是储存的值,没有符号,而非叶节点初始状态下没有值,但有符号。

可以利用stack容器来处理后缀表达式

建立一个date数组来记录下标为x的node类型,与stack联系起来

我们采用指针的形式定义各种数据,因为这样我们能动态分配内存,减少占用量。但更为关键的是,这样可以使我们建立各种数据结构建立起联系,在某一值改变的同时,其相应指针的其他类型也随之改变。

对待这道题的核心算法有两种方法

第一种

        就是每次输入要改变的值,然后遍历一遍整个树,给出答案。但显然在查询次数较多的情况下,这种方法肯定会超时。

第二种

       就是在输入要改变的值之前,我们打表:

如何打表呢,首先我们要Dfs从下而上遍历整个树,求出每个节点的默认值。

让后我们再定义一个函数Dfs_dp从上而下遍历整个树,求出每个节点的dp值。

dp[]数组的含义为当这个节点的值为x,整个树的运算结果为dp[x],dp显然只有两个状态,所以我们定义成 int dp[2]就足够了。

自上而下,对于根节点,显然当它的dp值就是整个树的值。

对于每个子节点,我们考虑两种情况:

1.父节点对应的符号为!时,它的dp值就是其父节点相反的dp值。

2.父节点对应的符号为& 或 |时,首先求出当它的值为x时,与其父节点的另外一个子节点进行相应符号运算的结果z,然后对应其相应父节点的dp[z]的值

这样更加快速的原因在于我们遍历完一遍树就可以求出所有情况的值,相较于方案一,我们不用多次遍历。

最后,输入每个要改变的节点时,我们输出改变后结果的dp值即可。

AC code is here

#include<cstdio>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
struct node{char op;int value;int dp[2];vector<node*>son;node(){dp[0]=0;dp[1]=0;value=0;op='F';vector<node*>son();}
};
stack<node*>fuel;                    
node* date[100000];
char str[100000];
int tot=0,quenum,que;
using namespace std;
int Dfs_solve(node* x){if(x->op=='F'){return 0;}for(int i=0;i<x->son.size();i++){Dfs_solve(x->son[i]);}if(x->op=='!'){x->value=(!x->son[0]->value);}else if(x->op=='&'){x->value=(x->son[0]->value & x->son[1]->value); }else if(x->op=='|'){x->value=(x->son[0]->value | x->son[1]->value); }return 0;
}
void Dfs_dp(node* x,node* father){if(father==NULL){x->dp[0]=0;x->dp[1]=1;}else{if(father->op=='!'){x->dp[0]=father->dp[1];x->dp[1]=father->dp[0]; }else if(father->op=='&' || father->op=='|'){node *other;if(father->son[0]==x){other=father->son[1];}else{other=father->son[0];}if(father->op=='&'){x->dp[0]=father->dp[0];x->dp[1]=father->dp[other->value]; }else{x->dp[1]=father->dp[1];x->dp[0]=father->dp[other->value];}}}for(int i=0;i<x->son.size();i++){Dfs_dp(x->son[i],x);}return;
}
int main(){while(scanf("%s",str)){if(str[0]=='x'){int num=0;for(int i=1;i<strlen(str);i++){num=(num<<3)+(num<<1)+(str[i]^48);}date[num]=new node;fuel.push(date[num]);}else if(str[0]=='&' || str[0]=='|'){node* a;node* b;node* c=new node;a=fuel.top();fuel.pop();b=fuel.top();fuel.pop();c->op=str[0];c->son.push_back(a);c->son.push_back(b);fuel.push(c);}else if(str[0]=='!'){node* a;node* b=new node;a=fuel.top();fuel.pop();b->son.push_back(a);b->op='!';fuel.push(b);}else{for(int i=0;i<strlen(str);i++){tot=(tot<<3)+(tot<<1)+(str[i]^48);}for(int i=1;i<=tot;i++){scanf("%d",&date[i]->value);}break;}}node* root=fuel.top();Dfs_solve(root);Dfs_dp(root,NULL);scanf("%d",&quenum);for(int i=0;i<quenum;i++){scanf("%d",&que);printf("%d\n",date[que]->dp[!date[que]->value]);}return 0;
}

END~~~

写作不易,若有不懂的地方欢迎评论,我会立刻回复。

谢谢大家的支持

http://www.dtcms.com/a/342294.html

相关文章:

  • Django的生命周期
  • 如何在DHTMLX Scheduler中实现带拖拽的任务待办区(Backlog)
  • 非常飘逸的 Qt 菜单控件
  • logger级别及大小
  • 如何安装和配置W3 Total Cache以提升WordPress网站性能
  • C++设计模式--策略模式与观察者模式
  • 小红书AI落地与前端开发技术全解析(From AI)
  • Python 正则表达式(更长的正则表达式示例)
  • 【基础排序】CF - 赌场游戏Playing in a Casino
  • 机器学习4
  • 精算中的提升曲线(Lift Curve)与机器学习中的差别
  • 网络打印机安装操作指南
  • 健康常识查询系统|基于java和小程序的健康常识查询系统设计与实现(源码+数据库+文档)
  • CentOS7安装部署PostgreSQL
  • 《PostgreSQL内核学习:slot_deform_heap_tuple 的分支消除与特化路径优化》
  • ES_文档
  • 2025-08-21 Python进阶6——迭代器生成器与with
  • Python项目开发- 动态设置工作目录与模块搜索路径
  • strerror和perror函数的使用及其联系和区别
  • 43-Python基础语法-3
  • QWidget/QMainWindow与QLayout的布局
  • CSDN使用技巧
  • Pandas中数据分组进阶以及数据透视表
  • 链表-143.重排链表-力扣(LeetCode)
  • 微信小程序集成vant-weapp时,构建npm报错的解决办法
  • 基于springboot的中医养生管理系统
  • Pytorch基础学习--张量(生成,索引,变形)
  • 火语言 RPA 进阶功能:让自动化更实用​
  • 交易高光时刻-01
  • SOP到自动化:一种适合小型金融机构的轻量级开源工具整合方案