You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010, M = N << 1;
int n; // 变量个数
int w[N]; // 变量参数的值
char c[N]; // 操作符栈
int stk[N], tt; // 数字栈
bool st[N]; // 是不是对
// 邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 第一次dfs求原后缀表达式,记录每个节点的计算值
int dfs1(int u) {
if (u <= n) return w[u]; // 叶子节点,返回参数值
if (c[u] == '!') // 如果当前节点是运算符!的话那么它一定只有一个子节点节点号h[u],值e[h[u]],取反返回即可
w[u] = !dfs1(e[h[u]]);
else {
// 与和或
int a = e[h[u]], b = e[ne[h[u]]]; // 取当前节点左儿子和右儿子
if (c[u] == '&')
w[u] = dfs1(a) & dfs1(b); // 左儿子与右儿子的与运算结果 
else
w[u] = dfs1(a) | dfs1(b); // 左儿子与右儿子的或运算结果 
}
return w[u]; // 返回计算结果值
}
// 从根开始,标记哪些节点影响表达式的值
void dfs2(int u) {
st[u] = true; // 因为
if (u <= n) return; // 递归到叶子返回
if (c[u] == '!') { // 取反操作符
dfs2(e[h[u]]); // 前进,继续标记节点
return;
}
int a = e[h[u]], b = e[ne[h[u]]]; // 左右儿子节点号
if (c[u] == '&') { // &运算
if (w[a]) dfs2(b); // 左儿子=1对右子树递归标记
if (w[b]) dfs2(a); // 右儿子=1对左子树递归标记
} else { // |运算
if (!w[a]) dfs2(b); // 左儿子=0递归右子树,右子树中的某些节点变化会对根造成影响
if (!w[b]) dfs2(a); // 右儿子=0递归左儿子,左子树中的某些节点变化会对根造成影响
}
}
int main() {
string s;
getline(cin, s);
cin >> n; // 参数个数
for (int i = 1; i <= n; i++) scanf("%d", &w[i]); // 每个参数对应的数值
memset(h, -1, sizeof h); // 邻接表初始化
// 为了创建一个表达式树就需要给每个节点创建一个编号。现在已知数字节点也就是叶子节点数量是n
// 所以,运算符的编号就是从++m开始的。
int m = n;
// 后缀表达式->栈->建图
// 利用栈进行辅助建图,图才能进行dfs计算
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') continue;
if (s[i] == 'x') {
int k = 0;
i++; // 跳过x
while (i < s.size() && isdigit(s[i])) k = k * 10 + s[i++] - '0';
stk[++tt] = k;
} else if (s[i] == '!') {
c[++m] = s[i]; //++m这个节点表达式树中是s[i]这个操作符,
/* 表达式树:
(1)每个叶子节点的数值
(2)非叶子节点需要记录是什么操作符
记录办法
(1)以树中的节点编号为索引,[1~n]为叶子,[n+1~]为操作符
(2)再开一个数组char c[],记录操作符节点是哪个操作符
*/
add(m, stk[tt--]); // 从栈中弹出一个数字,因为是!嘛,树是由上到下的连单向边
stk[++tt] = m; // m节点入栈方便后续构建
} else {
c[++m] = s[i];
add(m, stk[tt--]); // 与!不同需要由m引向两个节点各一条边
add(m, stk[tt--]);
stk[++tt] = m;
}
}
// 计算原式结果
int res = dfs1(m); // 这个m才是根因为是后缀表达式
// 标记哪些节点影响最终结果
dfs2(m);
for (int i = 1; i <= m; i++) {
cout << "i=" << i << ",st[" << i << "]=" << st[i] << ",c[" << i << "]=" << c[i] << endl;
}
int Q;
cin >> Q; // 询问个数
while (Q--) {
int x;
cin >> x; // 修改哪个变量
if (st[x]) // 如果x被打过标记那么它的变化将会影响根节点的值对根节点取反即可
printf("%d\n", !res);
else // 不会影响根节点的值
printf("%d\n", res);
}
return 0;
}