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 and111
0-2
010
0-1
-1任意-1
-2任意-2
o r or or11-1
01
01-2
01
-1任意-1
任意-2
n o t not not1/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;
}
posted @ 2020-11-10 21:29  AnAn_119  阅读(365)  评论(0编辑  收藏  举报