本场比赛最难的题目,难度远远大于T4,其实是一道模拟+结论题。
首先输入的一坨字符串要先解析,利用栈来建表达式树,这就是一个小模拟,相信正常人都会吧。
对于非运算,我直接用德·摩根定律,下传标记让子树信息都反一下。(其实没必要,当初这样写是因为觉得每个结点都二叉比较美,方便后续处理)
题目里有个信息是“每个变量在表达式中出现恰好一次”,而每个询问只改变一个变量的值,这对原答案来说就产生两个可能:变或不变。这听起来是一句废话,其实蕴含的意思是:有些变量对整个表达式其决定作用,其改变则原答案也改变;有些变量对整个表达式根本没用,其变不变原答案都不变。
说明白一点,就是1 & x = x
,0 | x= x
,这两个公式里的 xx 就起了决定性作用,而 0 & x = 0
,1 | x= 1
的 xx 就是一个废物。
那我们就给树上每个结点建一个废物标记,对&
来说,如果一棵子树是 00,那另外一棵子树内所有叶结点都应该打上废物标记,对|
同理。
先计算出原表示答案ans
,这样我们在查询的时候,没被标记的就说明它往上到根节点都不存在一种让它变成废物的运算,所以答案是!ans
,如果有标记则答案依旧为ans
。
时间复杂度 O(n+q)O(n+q)。
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int INF = 0x3f3f3f3f; const LL mod = 1e9 + 7; const int N = 1000005; char s[N]; int a[N]; int son[N][2], ck; int flag[N], c[N]; int n, q; int dfs(int u, int g) { a[u] ^= g; if (u <= n) { return a[u]; } int x = dfs(son[u][0], g ^ flag[son[u][0]]); int y = dfs(son[u][1], g ^ flag[son[u][1]]); if (a[u] == 2) { if (x == 0) c[son[u][1]] = 1; if (y == 0) c[son[u][0]] = 1; return x & y; } else { if (x == 1) c[son[u][1]] = 1; if (y == 1) c[son[u][0]] = 1; return x | y; } } void dfs2(int u) { if (u <= n) return; c[son[u][0]] |= c[u]; c[son[u][1]] |= c[u]; dfs2(son[u][0]); dfs2(son[u][1]); } int main() { // freopen("expr.in", "r", stdin); // freopen("expr.out", "w", stdout); gets(s); scanf("%d", &n); ck = n; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } stack<int> b; for (int i = 0; s[i]; i += 2) { if (s[i] == ‘x‘) { int x = 0; i++; while (s[i] != ‘ ‘) { x = x * 10 + s[i] - ‘0‘; i++; } i--; b.push(x); } else if (s[i] == ‘&‘) { int x = b.top(); b.pop(); int y = b.top(); b.pop(); b.push(++ck); a[ck] = 2; son[ck][0] = x; son[ck][1] = y; } else if (s[i] == ‘|‘) { int x = b.top(); b.pop(); int y = b.top(); b.pop(); b.push(++ck); a[ck] = 3; son[ck][0] = x; son[ck][1] = y; } else if(s[i] == ‘!‘){ flag[b.top()] ^= 1; } } int ans = dfs(ck, flag[ck]); dfs2(ck); scanf("%d", &q); while (q--) { int x; scanf("%d", &x); printf("%d\n", c[x] ? ans : !ans); } return 0; }
原文:https://www.cnblogs.com/pxy071128fzm/p/14032262.html