1. 程式人生 > 實用技巧 >3.表示式 題解

3.表示式 題解

本場比賽最難的題目,難度遠遠大於T4,其實是一道模擬+結論題。

首先輸入的一坨字串要先解析,利用棧來建表示式樹,這就是一個小模擬,相信正常人都會吧。

對於非運算,我直接用德·摩根定律,下傳標記讓子樹資訊都反一下。(其實沒必要,當初這樣寫是因為覺得每個結點都二叉比較美,方便後續處理)

題目裡有個資訊是“每個變數在表示式中出現恰好一次”,而每個詢問只改變一個變數的值,這對原答案來說就產生兩個可能:變或不變。這聽起來是一句廢話,其實蘊含的意思是:有些變數對整個表示式其決定作用,其改變則原答案也改變;有些變數對整個表示式根本沒用,其變不變原答案都不變。

說明白一點,就是1 & x = x0 | x= x

,這兩個公式裡的xx就起了決定性作用,而0 & x = 01 | x= 1xx就是一個廢物。

那我們就給樹上每個結點建一個廢物標記,對&來說,如果一棵子樹是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; }