zoukankan      html  css  js  c++  java
  • 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 != '
    ') 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
    ", (a[x] != ans[x] && ans[x] != -2));
    	}
    	return 0;
    }
    
  • 相关阅读:
    磁盘分区,fdisk,gdisk,开机自动挂载,swap分区,修复文件系统,备份文件
    进程脱离窗口运行,僵尸、孤儿进程
    top命令、kill命令
    进程状态
    rpm包、挂载、yum命令
    DRF源码分析
    forms组件源码
    Django CBV源码分析
    魔法方法
    鸭子类型
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279508.html
Copyright © 2011-2022 走看看