|
|
### [$AcWing$ $4730$. 逻辑表达式](https://www.acwing.com/problem/content/description/4733/)
|
|
|
|
|
|
### 一、题目描述
|
|
|
|
|
|
逻辑表达式是计算机科学中的重要概念和工具,包含逻辑值、逻辑运算、逻辑运算优先级等内容。
|
|
|
|
|
|
在一个逻辑表达式中,元素的值只有两种可能:$0$(表示假)和 $1$(表示真)。元素之间有多种可能的逻辑运算,本题中只需考虑如下两种:“与”(符号为 `&`)和“或”(符号为 `|`)。其运算规则如下:
|
|
|
|
|
|
$0 \mathbin{\&} 0 = 0 \mathbin{\&} 1 = 1 \mathbin{\&} 0 = 0$,$1 \mathbin{\&} 1 = 1$;
|
|
|
$0 \mathbin{|} 0 = 0$,$0 \mathbin{|} 1 = 1 \mathbin{|} 0 = 1 \mathbin{|} 1 = 1$。
|
|
|
|
|
|
在一个逻辑表达式中还可能有括号。规定在运算时,括号内的部分先运算;两种运算并列时,`&` 运算优先于 `|` 运算;同种运算并列时,从左向右运算。
|
|
|
|
|
|
比如,表达式 `0|1&0` 的运算顺序等同于 `0|(1&0)`;表达式 `0&1&0|1` 的运算顺序等同于 `((0&1)&0)|1`。
|
|
|
|
|
|
此外,在 $C++$ 等语言的有些编译器中,对逻辑表达式的计算会采用一种 **短路** 的策略:在形如 `a&b` 的逻辑表达式中,会先计算 `a` 部分的值,如果 $a = 0$,那么整个逻辑表达式的值就一定为 $0$,故无需再计算 `b` 部分的值;同理,在形如 `a|b` 的逻辑表达式中,会先计算 `a` 部分的值,如果 $a = 1$,那么整个逻辑表达式的值就一定为 $1$,无需再计算 `b` 部分的值。
|
|
|
|
|
|
现在给你一个逻辑表达式,你需要计算出它的值,并且统计出在计算过程中,两种类型的 **短路** 各出现了多少次。需要注意的是,如果某处 **短路** 包含在更外层被 **短路** 的部分内则不被统计,如表达式 `1|(0&1)` 中,尽管 `0&1` 是一处 **短路**,但由于外层的 `1|(0&1)` 本身就是一处 **短路**,无需再计算 `0&1` 部分的值,因此不应当把这里的 `0&1` 计入一处 **短路**。
|
|
|
|
|
|
#### 输入格式
|
|
|
|
|
|
输入共一行,一个非空字符串 $s$ 表示待计算的逻辑表达式。
|
|
|
|
|
|
#### 输出格式
|
|
|
|
|
|
输出共两行,第一行输出一个字符 `0` 或 `1`,表示这个逻辑表达式的值;第二行输出两个非负整数,分别表示计算上述逻辑表达式的过程中,形如 `a&b` 和 `a|b` 的“短路”各出现了多少次。
|
|
|
|
|
|
|
|
|
#### 样例输入 #1
|
|
|
|
|
|
```
|
|
|
0&(1|0)|(1|1|1&0)
|
|
|
```
|
|
|
|
|
|
#### 样例输出 #1
|
|
|
|
|
|
```
|
|
|
1
|
|
|
1 2
|
|
|
```
|
|
|
|
|
|
|
|
|
#### 样例输入 #2
|
|
|
|
|
|
```
|
|
|
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
|
|
|
```
|
|
|
|
|
|
#### 样例输出 #2
|
|
|
|
|
|
```
|
|
|
0
|
|
|
2 3
|
|
|
```
|
|
|
|
|
|
|
|
|
**【样例解释 \#1】**
|
|
|
|
|
|
该逻辑表达式的计算过程如下,每一行的注释表示上一行计算的过程:
|
|
|
|
|
|
```plain
|
|
|
0&(1|0)|(1|1|1&0)
|
|
|
=(0&(1|0))|((1|1)|(1&0)) //用括号标明计算顺序
|
|
|
=0|((1|1)|(1&0)) //先计算最左侧的 &,是一次形如 a&b 的“短路”
|
|
|
=0|(1|(1&0)) //再计算中间的 |,是一次形如 a|b 的“短路”
|
|
|
=0|1 //再计算中间的 |,是一次形如 a|b 的“短路”
|
|
|
=1
|
|
|
```
|
|
|
|
|
|
**【样例 \#3】**
|
|
|
|
|
|
见附件中的 `expr/expr3.in` 与 `expr/expr3.ans`。
|
|
|
|
|
|
**【样例 \#4】**
|
|
|
|
|
|
见附件中的 `expr/expr4.in` 与 `expr/expr4.ans`。
|
|
|
|
|
|
**【数据范围】**
|
|
|
|
|
|
设 $\lvert s \rvert$ 为字符串 $s$ 的长度。
|
|
|
|
|
|
对于所有数据,$1 \le \lvert s \rvert \le {10}^6$。保证 $s$ 中仅含有字符 `0`、`1`、`&`、`|`、`(`、`)` 且是一个符合规范的逻辑表达式。保证输入字符串的开头、中间和结尾均无额外的空格。保证 $s$
|
|
|
中没有重复的括号嵌套(即没有形如 `((a))` 形式的子串,其中 `a` 是符合规范的逻辑表
|
|
|
达式)。
|
|
|
|
|
|
| 测试点编号 | $\lvert s \rvert \le$ | 特殊条件 |
|
|
|
| :----------: | :-------------------: | :------: |
|
|
|
| $1 \sim 2$ | $3$ | 无 |
|
|
|
| $3 \sim 4$ | $5$ | 无 |
|
|
|
| $5$ | $2000$ | 1 |
|
|
|
| $6$ | $2000$ | 2 |
|
|
|
| $7$ | $2000$ | 3 |
|
|
|
| $8 \sim 10$ | $2000$ | 无 |
|
|
|
| $11 \sim 12$ | ${10}^6$ | 1 |
|
|
|
| $13 \sim 14$ | ${10}^6$ | 2 |
|
|
|
| $15 \sim 17$ | ${10}^6$ | 3 |
|
|
|
| $18 \sim 20$ | ${10}^6$ | 无 |
|
|
|
|
|
|
其中:
|
|
|
特殊性质 1 为:保证 $s$ 中没有字符 `&`。
|
|
|
特殊性质 2 为:保证 $s$ 中没有字符 `|`。
|
|
|
特殊性质 3 为:保证 $s$ 中没有字符 `(` 和 `)`。
|
|
|
|
|
|
**【提示】**
|
|
|
|
|
|
以下给出一个“符合规范的逻辑表达式”的形式化定义:
|
|
|
|
|
|
- 字符串 `0` 和 `1` 是符合规范的;
|
|
|
- 如果字符串 `s` 是符合规范的,且 `s` 不是形如 `(t)` 的字符串(其中 `t` 是符合规范的),那么字符串 `(s)` 也是符合规范的;
|
|
|
- 如果字符串 `a` 和 `b` 均是符合规范的,那么字符串 `a&b`、`a|b` 均是符合规范的;
|
|
|
- 所有符合规范的逻辑表达式均可由以上方法生成。
|
|
|
|
|
|
### 二、题目解析
|
|
|
|
|
|
本题与 $NOIP2013$普及组复赛第二题《**[表达式求值](https://www.acwing.com/problem/content/456/)**》是亲属关系,
|
|
|
|
|
|
#### 关键词
|
|
|
中缀表达式转后缀表达式,后缀表达式求值
|
|
|
|
|
|
#### 前置试题
|
|
|
[$AcWing$ $3302$ 表达式求值](https://www.acwing.com/problem/content/3305/)
|
|
|
|
|
|
[【$2013$ $NOIP$普及组】表达式求值](http://ybt.ssoier.cn:8088/problem_show.php?pid=1962)
|
|
|
|
|
|
#### 1、中缀表达式转后缀表达式(四则运算+以空格隔开)
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
|
|
|
// 中缀表达式转后缀表达式
|
|
|
/*
|
|
|
测试用例1:
|
|
|
a+b*c+(d*e+f)*g
|
|
|
|
|
|
答案:
|
|
|
abc*+de*f+g*+
|
|
|
|
|
|
|
|
|
测试用例2:
|
|
|
(6+3*(7-4))-8/2
|
|
|
|
|
|
答案:
|
|
|
6 3 7 4 - * + 8 2 / -
|
|
|
|
|
|
测试用例3:
|
|
|
(24*(9+6/38-5)+4)
|
|
|
答案:
|
|
|
24 9 6 38 / + 5 - * 4 +
|
|
|
*/
|
|
|
unordered_map<char, int> h{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 运算符级别表
|
|
|
string s; // 源串
|
|
|
string t; // 目标串
|
|
|
stack<char> stk; // 利用一个栈,完成中缀表达式转后缀表达式
|
|
|
int main() {
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
// ①数字
|
|
|
if (isdigit(s[i])) {
|
|
|
int x = 0;
|
|
|
while (i < s.size() && isdigit(s[i])) {
|
|
|
x = x * 10 + s[i] - '0';
|
|
|
i++;
|
|
|
}
|
|
|
i--; // 走多了才能停止掉上面的while,需要回退一下
|
|
|
t.append(to_string(x));
|
|
|
} else if (isalpha(s[i])) // ②字符,比如a,b,c
|
|
|
t.push_back(s[i]);
|
|
|
else if (s[i] == '(') // ③左括号
|
|
|
stk.push(s[i]); // 左括号入栈
|
|
|
else if (s[i] == ')') { // ④右括号
|
|
|
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.pop(); // 左括号也需要出栈
|
|
|
} else {
|
|
|
// ⑤操作符 +-*/
|
|
|
while (stk.size() && h[s[i]] <= h[stk.top()]) { // 哪个操作符优先级高就先输出谁
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.push(s[i]); // 将自己入栈
|
|
|
}
|
|
|
}
|
|
|
// 当栈不为空时,全部输出
|
|
|
while (stk.size()) {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
// printf("%s", t.c_str());
|
|
|
cout << t << endl;
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### 2、中缀表达式转后缀表达式(逻辑运算符+拷贝四则版本)
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
|
|
|
/*
|
|
|
中缀的逻辑表达式 转 后缀的逻辑表达式
|
|
|
|
|
|
测试用例:
|
|
|
0&(0|1|0)
|
|
|
|
|
|
答案:
|
|
|
001|0|&
|
|
|
*/
|
|
|
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
|
|
|
string s;
|
|
|
string t;
|
|
|
stack<char> stk;
|
|
|
int main() {
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
if (isdigit(s[i])) {
|
|
|
int x = 0;
|
|
|
while (i < s.size() && isdigit(s[i])) {
|
|
|
x = x * 10 + s[i] - '0';
|
|
|
i++;
|
|
|
}
|
|
|
i--;
|
|
|
t.append(to_string(x));
|
|
|
} else if (isalpha(s[i]))
|
|
|
t.push_back(s[i]);
|
|
|
else if (s[i] == '(')
|
|
|
stk.push(s[i]);
|
|
|
else if (s[i] == ')') {
|
|
|
while (stk.top() != '(') {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.pop();
|
|
|
} else {
|
|
|
while (stk.size() && h[s[i]] <= h[stk.top()]) {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.push(s[i]);
|
|
|
}
|
|
|
}
|
|
|
while (stk.size()) {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
printf("%s", t.c_str());
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
#### 3、中缀表达式转后缀表达式(逻辑运算符+精简版本)
|
|
|
> **注**:因为逻辑运算,数字只有$0$和$1$,所以,`while`循环读取数字没用了,可以省略,当然,你非得要背模板写成一样的,也没有问题。
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
/*
|
|
|
中缀的逻辑表达式 转 后缀的逻辑表达式
|
|
|
|
|
|
测试用例:
|
|
|
0&(0|1|0)
|
|
|
|
|
|
答案:
|
|
|
001|0|&
|
|
|
*/
|
|
|
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
|
|
|
string s;
|
|
|
string t;
|
|
|
stack<char> stk;
|
|
|
|
|
|
int main() {
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
if (isdigit(s[i]) || isalpha(s[i]))
|
|
|
t.push_back(s[i]);
|
|
|
else if (s[i] == '(')
|
|
|
stk.push(s[i]);
|
|
|
else if (s[i] == ')') {
|
|
|
while (stk.top() != '(') {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.pop();
|
|
|
} else {
|
|
|
while (stk.size() && h[s[i]] <= h[stk.top()]) {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
stk.push(s[i]);
|
|
|
}
|
|
|
}
|
|
|
while (stk.size()) {
|
|
|
t.push_back(stk.top());
|
|
|
stk.pop();
|
|
|
}
|
|
|
printf("%s", t.c_str());
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
#### 5、中缀表达式求值(四则版本)
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
// OJ 测试:
|
|
|
// AcWing 3302. 表达式求值
|
|
|
// https://www.acwing.com/problem/content/3305/
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
using namespace std;
|
|
|
/*
|
|
|
中缀表达式求值
|
|
|
|
|
|
测试用例I:
|
|
|
(2+2)*(1+1)
|
|
|
|
|
|
答案:8
|
|
|
|
|
|
测试用例II:
|
|
|
2+(3*4)-((5*9-5)/8-4)
|
|
|
|
|
|
答案:13
|
|
|
*/
|
|
|
|
|
|
stack<int> num; //数字栈
|
|
|
stack<char> op; //操作符栈
|
|
|
|
|
|
//优先级表
|
|
|
unordered_map<char, int> h{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
|
|
|
|
|
|
/**
|
|
|
* 功能:计算两个数的和差积商
|
|
|
*/
|
|
|
void eval() {
|
|
|
int a = num.top(); //第二个操作数
|
|
|
num.pop();
|
|
|
|
|
|
int b = num.top(); //第一个操作数
|
|
|
num.pop();
|
|
|
|
|
|
char p = op.top(); //运算符
|
|
|
op.pop();
|
|
|
|
|
|
int r; //结果
|
|
|
//计算结果
|
|
|
if (p == '+')
|
|
|
r = b + a;
|
|
|
else if (p == '-')
|
|
|
r = b - a;
|
|
|
else if (p == '*')
|
|
|
r = b * a;
|
|
|
else if (p == '/')
|
|
|
r = b / a;
|
|
|
//结果入栈
|
|
|
num.push(r);
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
//读入表达式
|
|
|
string s;
|
|
|
cin >> s;
|
|
|
//遍历字符串的每一位
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
//① 如果是数字,则入栈
|
|
|
if (isdigit(s[i])) {
|
|
|
//读出完整的数字
|
|
|
int x = 0;
|
|
|
while (i < s.size() && isdigit(s[i])) {
|
|
|
x = x * 10 + s[i] - '0';
|
|
|
i++;
|
|
|
}
|
|
|
i--; //加多了一位,需要减去
|
|
|
|
|
|
num.push(x); //数字入栈
|
|
|
}
|
|
|
//② 左括号无优先级,入栈
|
|
|
else if (s[i] == '(')
|
|
|
op.push(s[i]);
|
|
|
//③ 右括号时,需计算最近一对括号里面的值
|
|
|
else if (s[i] == ')') {
|
|
|
//从栈中向前找,一直找到左括号
|
|
|
while (op.top() != '(') eval(); //将左右括号之间的计算完,维护回栈里
|
|
|
//左括号出栈
|
|
|
op.pop();
|
|
|
} else { //④ 运算符
|
|
|
//如果待入栈运算符优先级低,则先计算
|
|
|
while (op.size() && h[op.top()] >= h[s[i]]) eval();
|
|
|
op.push(s[i]); //操作符入栈
|
|
|
}
|
|
|
}
|
|
|
while (op.size()) eval(); //⑤ 剩余的进行计算
|
|
|
printf("%d\n", num.top()); //输出结果
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
#### 6、中缀表达式求值(逻辑表达式+拷贝四则版本)
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
|
|
|
/*
|
|
|
0&(1|0)|(1|1|1&0)
|
|
|
答案:1
|
|
|
|
|
|
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
|
|
|
答案:0
|
|
|
*/
|
|
|
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
|
|
|
stack<int> num;
|
|
|
stack<char> op;
|
|
|
|
|
|
void eval() {
|
|
|
int a = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
int b = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
char p = op.top();
|
|
|
op.pop();
|
|
|
|
|
|
int r;
|
|
|
if (p == '|')
|
|
|
r = b | a;
|
|
|
else if (p == '&')
|
|
|
r = b & a;
|
|
|
num.push(r);
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
string s;
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
if (isdigit(s[i])) {
|
|
|
int x = 0;
|
|
|
while (i < s.size() && isdigit(s[i])) {
|
|
|
x = x * 10 + s[i] - '0';
|
|
|
i++;
|
|
|
}
|
|
|
i--;
|
|
|
|
|
|
num.push(x);
|
|
|
} else if (s[i] == '(')
|
|
|
op.push(s[i]);
|
|
|
else if (s[i] == ')') {
|
|
|
while (op.top() != '(') eval();
|
|
|
op.pop();
|
|
|
} else {
|
|
|
while (op.size() && h[op.top()] >= h[s[i]]) eval();
|
|
|
op.push(s[i]);
|
|
|
}
|
|
|
}
|
|
|
while (op.size()) eval();
|
|
|
|
|
|
printf("%d\n", num.top());
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
#### 7、中缀表达式求值(逻辑表达式+简化版本)
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
|
|
|
/*
|
|
|
0&(1|0)|(1|1|1&0)
|
|
|
答案:1
|
|
|
|
|
|
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
|
|
|
答案:0
|
|
|
*/
|
|
|
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
|
|
|
stack<int> num;
|
|
|
stack<char> op;
|
|
|
|
|
|
void eval() {
|
|
|
int a = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
int b = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
char p = op.top();
|
|
|
op.pop();
|
|
|
|
|
|
int r;
|
|
|
if (p == '|')
|
|
|
r = b | a;
|
|
|
else if (p == '&')
|
|
|
r = b & a;
|
|
|
num.push(r);
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
string s;
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
if (isdigit(s[i]))
|
|
|
num.push(s[i] - '0');
|
|
|
else if (s[i] == '(')
|
|
|
op.push(s[i]);
|
|
|
else if (s[i] == ')') {
|
|
|
while (op.top() != '(') eval();
|
|
|
op.pop();
|
|
|
} else {
|
|
|
while (op.size() && h[op.top()] >= h[s[i]]) eval();
|
|
|
op.push(s[i]);
|
|
|
}
|
|
|
}
|
|
|
while (op.size()) eval();
|
|
|
|
|
|
printf("%d\n", num.top());
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
铺垫的知识完成,现在开始分析本题:
|
|
|
|
|
|
* **中缀逻辑表达式求值**
|
|
|
* **记录短路次数**
|
|
|
|
|
|
#### 规律总结
|
|
|
用一个三元组来替换原版本放在栈里的$int$,即:
|
|
|
$Node(v,a,b)$,代表:当前数字值是$v$,已经计算过的$\&$短路次数是$a$,已经计算过的$|$短路次数是$b$
|
|
|
|
|
|
找一个思路相似的简单问题给大家看看:
|
|
|

|
|
|
|
|
|
则有下面的递推式:
|
|
|
$\large (1,a_1,b_1) | (?,a_2,b_2) \Rightarrow (1,a_1,b_1+1) 发生了短路运算,后面的不再计算$
|
|
|
$\large (0,a_1,b_1) | (?,a_2,b_2) \Rightarrow (?,a_1+a_2,b_1+b_2) 没有发生短路计算$
|
|
|
$\large (1,a_1,b_1) \& (?,a_2,b_2) \Rightarrow (?,a_1+a_2,b_1+b_2) 没有发生短路计算$
|
|
|
$\large (0,a_1,b_1) \& (?,a_2,b_2) \Rightarrow (0,a_1+1,b_1) 发生了短路运算,后面的不再计算$
|
|
|
|
|
|
**实现代码**:
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
/*
|
|
|
测试用例1:
|
|
|
0&(1|0)|(1|1|1&0)
|
|
|
答案:
|
|
|
1
|
|
|
1 2
|
|
|
|
|
|
测试用例2:
|
|
|
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
|
|
|
答案:
|
|
|
0
|
|
|
2 3
|
|
|
*/
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
|
|
|
struct Node {
|
|
|
int v, a, b; // v:代表当前的结果值,a: &短路的次数 b:|短路的次数
|
|
|
};
|
|
|
|
|
|
stack<Node> num;
|
|
|
stack<char> stk;
|
|
|
|
|
|
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
|
|
|
|
|
|
void eval() {
|
|
|
// 这里要注意从栈中弹出元素的顺序,先出来的是y右子树,后出来的是x左子树
|
|
|
Node y = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
Node x = num.top();
|
|
|
num.pop();
|
|
|
|
|
|
char p = stk.top();
|
|
|
stk.pop();
|
|
|
|
|
|
Node r;
|
|
|
if (p == '|') {
|
|
|
if (x.v == 1)
|
|
|
r = {x.v, x.a, x.b + 1};
|
|
|
else
|
|
|
r = {y.v, x.a + y.a, x.b + y.b};
|
|
|
} else {
|
|
|
if (x.v == 1)
|
|
|
r = {y.v, x.a + y.a, x.b + y.b};
|
|
|
else
|
|
|
r = {x.v, x.a + 1, x.b};
|
|
|
}
|
|
|
num.push(r);
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
string s;
|
|
|
cin >> s;
|
|
|
for (int i = 0; i < s.size(); i++) {
|
|
|
if (isdigit(s[i])) {
|
|
|
int x = 0;
|
|
|
while (i < s.size() && isdigit(s[i])) {
|
|
|
x = x * 10 + s[i] - '0';
|
|
|
i++;
|
|
|
}
|
|
|
i--;
|
|
|
num.push({x, 0, 0});
|
|
|
} else if (s[i] == '(')
|
|
|
stk.push(s[i]);
|
|
|
else if (s[i] == ')') {
|
|
|
while (stk.top() != '(') eval();
|
|
|
stk.pop();
|
|
|
} else {
|
|
|
while (stk.size() && h[stk.top()] >= h[s[i]]) eval();
|
|
|
stk.push(s[i]);
|
|
|
}
|
|
|
}
|
|
|
while (stk.size()) eval();
|
|
|
|
|
|
printf("%d\n", num.top().v);
|
|
|
printf("%d %d\n", num.top().a, num.top().b);
|
|
|
return 0;
|
|
|
}
|
|
|
``` |