目录
题目
题解:
题目
小 C 热衷于学习数理逻辑。
有一天,他发现了一种特别的逻辑表达式。
在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为 0 或 1,运算从左往右进行。
如果表达式中有括号,则先计算括号内的子表达式的值。
特别的,这种表达式有且仅有以下几种运算:
- 与运算:푎 & 푏。当且仅当 푎 和 푏 的值都为 1 时,该表达式的值为 1。其余情况该表达式的值为 00。
- 或运算:푎 | 푏。当且仅当 푎 和 푏 的值都为 0 时,该表达式的值为 0。其余情况该表达式的值为 1。
- 取反运算:!푎。当且仅当 푎 的值为 0 时,该表达式的值为 1。其余情况该表达式的值为 0。
小 C 想知道,给定一个逻辑表达式和其中每一个操作数的初始取值后,再取反某一个操作数的值时,原表达式的值为多少。
为了化简对表达式的处理,我们有如下约定:表达式将采用后缀表达式的方式输入。
后缀表达式的定义如下:
- 如果 퐸 是一个操作数,则 E퐸 的后缀表达式是它本身。
- 如果 퐸 是 퐸1 표푝 퐸2 形式的表达式,其中 표푝 是任何二元操作符,且优先级不高于 퐸1、퐸2 中括号外的操作符,则 E퐸 的后缀式为 퐸1′ 퐸2′ 표푝,其中 퐸1′、퐸2′ 分别为 퐸1、퐸2 的后缀式。
- 如果 퐸 是 (퐸1) 形式的表达式,则 퐸1 的后缀式就是 퐸 的后缀式。
同时为了方便,输入中:
a) 与运算符(&)、或运算符(|)、取反运算符(!)的左右均有一个空格,但表达式末尾没有空格。
b) 操作数由小写字母 x 与一个正整数拼接而成,正整数表示这个变量的下标。例如:x10,表示下标为 10 的变量 푥10。
数据保证每个变量在表达式中出现恰好一次。
输入格式
第一行包含一个字符串 푠,表示上文描述的表达式。
第二行包含一个正整数 푛,表示表达式中变量的数量。表达式中变量的下标为 1,2,…,푛。
第三行包含 푛 个整数,第 푖 个整数表示变量 푥푖 的初值。
第四行包含一个正整数 푞,表示询问的个数。
接下来 푞 行,每行一个正整数,表示需要取反的变量的下标。
注意,每一个询问的修改都是临时的,即之前询问中的修改不会对后续的询问造成影响。
数据保证输入的表达式合法。
变量的初值为 0 或 1。
输出格式
输出一共有 푞 行,每行一个 0 或 1,表示该询问下表达式的值。
数据范围
对于 20% 的数据,表达式中有且仅有与运算(&)或者或运算(|)。
对于另外 30% 的数据,|푠|≤1000,푞≤1000,푛≤1000。
对于另外 20% 的数据,变量的初值全为 0 或全为 1。
对于 100% 的数据,1≤|푠|≤1×106,1≤푞≤1×105,2≤푛≤1×105。
其中,|푠| 表示字符串 푠 的长度。
输入样例1:
x1 x2 & x3 |
3
1 0 1
3
1
2
3
输出样例1:
1
1
0
样例1解释
该后缀表达式的中缀表达式形式为 (x1 & x2) | x3。
对于第一次询问,将 x1푥1 的值取反。此时,三个操作数对应的赋值依次为 0,0,1。原表达式的值为 (0 & 0) | 1=1。
对于第二次询问,将 x2푥2 的值取反。此时,三个操作数对应的赋值依次为 1,1,1。原表达式的值为 (1 & 1) | 1=1。
对于第三次询问,将 x3푥3 的值取反。此时,三个操作数对应的赋值依次为 1,0,0。原表达式的值为 (1 & 0) | 0=0。
输入样例2:
x1 ! x2 x4 | x3 x5 ! & & ! &
5
0 1 0 1 1
3
1
3
5
输出样例2:
0
1
1
样例2解释
该表达式的中缀表达式形式为 (!x1) & (!((x2 | x4) & (x3 & (!x5))))。
题解:
小 C 热衷于学习数理逻辑。
有一天,他发现了一种特别的逻辑表达式。
在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为 0 或 1,运算从左往右进行。
如果表达式中有括号,则先计算括号内的子表达式的值。
特别的,这种表达式有且仅有以下几种运算:
- 与运算:푎 & 푏。当且仅当 푎 和 푏 的值都为 1 时,该表达式的值为 1。其余情况该表达式的值为 00。
- 或运算:푎 | 푏。当且仅当 푎 和 푏 的值都为 0 时,该表达式的值为 0。其余情况该表达式的值为 1。
- 取反运算:!푎。当且仅当 푎 的值为 0 时,该表达式的值为 1。其余情况该表达式的值为 0。
小 C 想知道,给定一个逻辑表达式和其中每一个操作数的初始取值后,再取反某一个操作数的值时,原表达式的值为多少。
为了化简对表达式的处理,我们有如下约定:表达式将采用后缀表达式的方式输入。
后缀表达式的定义如下:
- 如果 퐸 是一个操作数,则 E퐸 的后缀表达式是它本身。
- 如果 퐸 是 퐸1 표푝 퐸2 形式的表达式,其中 표푝 是任何二元操作符,且优先级不高于 퐸1、퐸2 中括号外的操作符,则 E퐸 的后缀式为 퐸1′ 퐸2′ 표푝,其中 퐸1′、퐸2′ 分别为 퐸1、퐸2 的后缀式。
- 如果 퐸 是 (퐸1) 形式的表达式,则 퐸1 的后缀式就是 퐸 的后缀式。
同时为了方便,输入中:
a) 与运算符(&)、或运算符(|)、取反运算符(!)的左右均有一个空格,但表达式末尾没有空格。
b) 操作数由小写字母 x 与一个正整数拼接而成,正整数表示这个变量的下标。例如:x10,表示下标为 10 的变量 푥10。
数据保证每个变量在表达式中出现恰好一次。
输入格式
第一行包含一个字符串 푠,表示上文描述的表达式。
第二行包含一个正整数 푛,表示表达式中变量的数量。表达式中变量的下标为 1,2,…,푛。
第三行包含 푛 个整数,第 푖 个整数表示变量 푥푖 的初值。
第四行包含一个正整数 푞,表示询问的个数。
接下来 푞 行,每行一个正整数,表示需要取反的变量的下标。
注意,每一个询问的修改都是临时的,即之前询问中的修改不会对后续的询问造成影响。
数据保证输入的表达式合法。
变量的初值为 0 或 1。
输出格式
输出一共有 푞 行,每行一个 0 或 1,表示该询问下表达式的值。
数据范围
对于 20% 的数据,表达式中有且仅有与运算(&)或者或运算(|)。
对于另外 30% 的数据,|푠|≤1000,푞≤1000,푛≤1000。
对于另外 20% 的数据,变量的初值全为 0 或全为 1。
对于 100% 的数据,1≤|푠|≤1×106,1≤푞≤1×105,2≤푛≤1×105。
其中,|푠| 表示字符串 푠 的长度。
输入样例1:
x1 x2 & x3 | 3 1 0 1 3 1 2 3
输出样例1:
1 1 0
样例1解释
该后缀表达式的中缀表达式形式为 (x1 & x2) | x3。
对于第一次询问,将 x1푥1 的值取反。此时,三个操作数对应的赋值依次为 0,0,1。原表达式的值为 (0 & 0) | 1=1。
对于第二次询问,将 x2푥2 的值取反。此时,三个操作数对应的赋值依次为 1,1,1。原表达式的值为 (1 & 1) | 1=1。
对于第三次询问,将 x3푥3 的值取反。此时,三个操作数对应的赋值依次为 1,0,0。原表达式的值为 (1 & 0) | 0=0。
输入样例2:
x1 ! x2 x4 | x3 x5 ! & & ! & 5 0 1 0 1 1 3 1 3 5
输出样例2:
0 1 1
样例2解释
该表达式的中缀表达式形式为 (!x1) & (!((x2 | x4) & (x3 & (!x5))))。
题解:
知识点:表达式求值、链式前向星
分析:
由题意得,题目的表达式是后缀表达式,所以可以用栈建立一棵表达式树,然后从根结点向下递归求出表达式树的值。
因为计算过程只会出现逻辑运算,所以就可以利用'&'、'|'运算的短路性质,预处理出每个结点u的左右儿子发生变化时,是否影响到了最终的计算结果,即表达式的值。这样,每次询问就能在O(1)的时间复杂度内返回答案。
预处理出每个结点u的左右儿子发生变化时,我们可以对根结点的影响可以使用两次DFS:
- 第一次DFS求出所有非叶子结点u的值w[u]和表达式的值
- 第二次DFS求所有非叶子结点的值发生变化时能够影响到根结点的值:
- 如果当前运算符是'!',子结点发生变化,就能影响到根结点的值
- 如果当前运算符是'&',两个子结点分别为a和b,如果w[a]是1,b结点发生变化,就能影响到根结点的值;如果w[b]是1,a结点发生变化,就能影响到根结点的值;
- 如果当前运算符是'|',两个子结点分别为a和b,如果w[a]是0,b结点发生变化,就能影响到根结点的值;如果w[b]是0,a结点发生变化,就能影响到根结点的值
代码:
#include#include #include #include #include #include #include #include using namespace std; const int N=1000010; int n,m,h[N],e[N],ne[N],idx,w[N];//n是叶节点数量,m是结点总数量 bool st[N];//标记某节点是否会影响答案 char c[N];//存每个节点符号,下标从n开始,1~n不存是为了方便表示叶节点 stack stk;//存每个节点的编号 inline void c_plus(){//提速 ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); } inline void init(){//预处理链式前向星 memset(h,-1,sizeof(h)); idx=0; } inline void add(int a,int b){//链式前向星,添加一条边a->b e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } int dfs1(int u){//求原表达式的值,u为当前节点编号 if (!c[u]){//如果该位置无符号(即如果是叶节点) return w[u];//直接返回其权值 }else if (c[u]=='!'){//如果是非运算 w[u]=!dfs1(e[h[u]]);//w[u]=将u递归求值后取反的值 }else{//处理二元运算符 int a=e[h[u]],b=e[ne[h[u]]];//a为左子节点,b为右子节点 if (c[u]=='&'){//如果是与运算 w[u]=dfs1(a)&dfs1(b);//同理,不解释了 }else{//如果是或运算 w[u]=dfs1(a)|dfs1(b); } } return w[u]; } void dfs2(int u){//标记哪些节点会影响答案 st[u]=1; if (!c[u]){//如果该位置无符号(即如果是叶节点) return;//无法继续下去 }else if (c[u]=='!'){//如果是非运算 dfs2(e[h[u]]);分析中已解释 }else{//处理二元运算符 int a=e[h[u]],b=e[ne[h[u]]];//a为其左子节点,b为右子节点 if (c[u]=='&'){//如果是与运算 if (w[a]){//分析中已解释 dfs2(b); } if (w[b]){//分析中已解释 dfs2(a); } }else{//如果是或运算 if (!w[a]){//分析中已解释 dfs2(b); } if (!w[b]){//分析中已解释 dfs2(a); } } } } int main(){ c_plus(); init(); string s; getline(cin,s);//因为字符串中有空格,所以用getline cin>>n; for (int i=1;i<=n;i++){ cin>>w[i]; } m=n;//m=n+非叶节点数量,所以让m先等于n,再加上非叶节点数量 for (int i=0;i >q; while (q--){ cin>>x; if (st[x]){//若该节点会影响答案 cout<<(ans^1)<


![C++题解:[CSP-J2020]表达式 C++题解:[CSP-J2020]表达式](http://www.mshxw.com/aiimages/31/330054.png)
