2020 CSP-J T3 表达式(栈+二叉树)
2020 CSP-J T3 表达式
题解
如何把后缀表达式转回普通表达式?
-
观察样例,不难发现, a n d and and和 o r or or运算符一定是插入在前面最近的两对括号之间, n o t not not则是在前面最近的一对括号前。括号是什么?每个变量两边可以看做有一对括号,每次运算结束后可以在两边加上一对括号。即“ ( x x x ) (xxx) (xxx) ( y y y ) (yyy) (yyy) a n d and and”可变为“ ( ( x x x ) \Big((xxx) ((xxx) a n d and and ( y y y ) ) (yyy)\Big) (yyy))”,其余同理。
-
那么直接把整个字符串从左往右扫一遍,遇到变量则压入栈中, a n d and and和 o r or or则弹出栈顶两个元素, n o t not not则弹出栈顶一个元素,再把运算符连同刚刚弹栈的部分看做是一个整体,一起压入栈中。
怎么维护询问?
-
在弹栈过程中,把整个表达式建成一棵二叉树,所有叶子结点记录变量的编号,非叶子结点记录运算符和左右儿子编号( n o t not not没有左儿子)。先遍历整棵树,求出每个子树的最初的值,记录在子树的根上。
-
单独维护每个变量修改复杂度过高,可以考虑整棵树一起做。把整棵树遍历一遍,求出使整个表达式的值为真时,每个子树需要满足什么条件,有四种情况,分别为:取 0 0 0,取 1 1 1,取 0 / 1 0/1 0/1均可(记为 − 1 -1 −1),取 0 / 1 0/1 0/1均不可(记为 − 2 -2 −2)。
-
具体操作可以在遍历过程中,记录该子树内需满足值为 x x x才能为使整个表达式值为真,按照运算规则继续递归下去,对不同的运算,需根据另一儿子原表达式的值的情况得出该儿子需满足怎样的条件,易得下表。
运算符 | 整个子树需满足的条件(递归到当前的x) | 左/右儿子的最初的值(已知) | 右/左儿子的条件(递归下去) |
---|---|---|---|
a n d and and | 1 | 1 | 1 |
0 | -2 | ||
0 | 1 | 0 | |
0 | -1 | ||
-1 | 任意 | -1 | |
-2 | 任意 | -2 | |
o r or or | 1 | 1 | -1 |
0 | 1 | ||
0 | 1 | -2 | |
0 | 1 | ||
-1 | 任意 | -1 | |
任意 | -2 | ||
n o t not not | 1 | / | 0 |
0 | / | 1 | |
-1 | / | -1 | |
-2 | / | -2 |
- 最后询问时直接比较每个变量取反后和需满足的条件是否符合即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000010
char st[N];
int tot = 0, a[N], ans[N], q[N];
struct {
int x, l = 0, r = 0, s;
}f[N];
// -1 not
// -2 and
// -3 or
void dfs(int v) {
if(f[v].x > 0) {
f[v].s = a[f[v].x];
return;
}
int l, r;
if(f[v].l) dfs(f[v].l), l = f[f[v].l].s;
if(f[v].r) dfs(f[v].r), r = f[f[v].r].s;
if(f[v].x == -1) f[v].s = 1 - r;
else if(f[v].x == -2) f[v].s = l & r;
else f[v].s = l | r;
}
void solve(int v, int x) {
if(f[v].x > 0) {
ans[f[v].x] = x;
return;
}
if(f[v].x == -1) {
x < 0 ? solve(f[v].r, x) : solve(f[v].r, 1 - x);
}
else if(f[v].x == -2) {
x < 0 ? solve(f[v].l, x) : (x == 1 ? solve(f[v].l, f[f[v].r].s == 0 ? -2 : 1) : solve(f[v].l, f[f[v].r].s == 0 ? -1 : 0));
x < 0 ? solve(f[v].r, x) : (x == 1 ? solve(f[v].r, f[f[v].l].s == 0 ? -2 : 1) : solve(f[v].r, f[f[v].l].s == 0 ? -1 : 0));
}
else if(f[v].x == -3) {
x < 0 ? solve(f[v].l, x) : (x == 1 ? solve(f[v].l, f[f[v].r].s == 1 ? -1 : 1) : solve(f[v].l, f[f[v].r].s == 1 ? -2 : 0));
x < 0 ? solve(f[v].r, x) : (x == 1 ? solve(f[v].r, f[f[v].l].s == 1 ? -1 : 1) : solve(f[v].r, f[f[v].l].s == 1 ? -2 : 0));
}
}
int main() {
int n, Q, i, j, x, ls = 0;
char c = getchar();
while(c != '\n') st[++ls] = c, c = getchar();
for(i = 1; i <= ls; i++) {
if(st[i] == 'x') {
j = i + 1, x = 0;
while(st[j] >= '0' && st[j] <= '9') x = x * 10 + st[j] - 48, j++;
i = j - 1;
f[++tot].x = x;
q[++q[0]] = tot;
}
else if(st[i] == '!') {
f[++tot].x = -1;
f[tot].r = q[q[0]], q[q[0]] = tot;
}
else if(st[i] == '&') {
f[++tot].x = -2;
f[tot].r = q[q[0]], f[tot].l = q[q[0] - 1];
q[0]--;
q[q[0]] = tot;
}
else if(st[i] == '|') {
f[++tot].x = -3;
f[tot].r = q[q[0]], f[tot].l = q[q[0] - 1];
q[0]--;
q[q[0]] = tot;
}
}
scanf("%d", &n);
for(i = 1; i <= n; i++) scanf("%d", &a[i]);
dfs(q[1]);
solve(q[1], 1);
scanf("%d", &Q);
while(Q--) {
scanf("%d", &x);
printf("%d\n", (a[x] != ans[x] && ans[x] != -2));
}
return 0;
}