洛谷P7073 [CSP-J2020]表达式(c++版)题解

题目:P7073 [CSP-J2020] 表达式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目看上方链接

乍一看这道题,大家都能想到是表达式树的题。

难点:在建好树、算好值后某一未知数取反后不重新算,如何更新答案?

  1. 第一次算的时候记录每一个子树的值(之后都叫vl)。
  2. 只有取反的未知数的祖先(包括他自己)的vl才有可能更改。
  3. 利用②,更新答案时向上追溯祖先并求出更新后对应的vl,我叫他tt。
  4. 如果追溯到某一祖先t==vl直接跳出,否则vl=t,继续追溯。

所以,问题完美解决,时间复杂度O\left(|s|+n+q\cdot log\left(n\right)\right),代码在下面

tree中:

  1. fa表示父亲节点,sl、sr表示的是孩子,“&”“|”有两个儿子(用到sl、sr),“!”只有一个(用到sl),未知数没有后代。
  2. vl就是上文提到的答案值。
  3. m表示杂乱信息:{kng是类型:kng==true时表示未知数(此时id是x后面的数);hng==false时表示符号(此时id是字符串中所对应的位置下标)}。
  4. 每个节点编号是cnt(即tree[cnt]).

st是建树时用到的栈(下标是ed),w[i]表示xi对应的cnt,r是树根

dfs用来求vl

#include
#define N 110000
#define SL 1100000
using namespace std;
struct Treenode {
	struct Inf {
		int id;
		bool knd;
	};
	Inf m;
	int fa, sl, sr;
	bool vl;
};
Treenode tree[SL] = {};
int st[SL] = {}, w[N] = {}, cnt = 0, ed = 0, n = 0, q = 0, r = 0;
char s[SL] = {};
inline void dfs(int node);
int main() {
	cin.getline(s + 1, SL);
	int l = strlen(s + 1);
	s[l + 1] = ' ';
	s[l + 1] = '\0';
	l++;
	for (int i = 1; i <= l; i++) {
		if (s[i] == '&' || s[i] == '|') {
			cnt++;
			tree[cnt].m.knd = true;
			tree[cnt].m.id = i;
			tree[cnt].sl = st[ed - 1];
			tree[cnt].sr = st[ed];
			tree[st[ed - 1]].fa = tree[st[ed]].fa = cnt;
			ed--;
			st[ed] = cnt;
		} else {
			if (s[i] == '!') {
				cnt++;
				tree[cnt].m.knd = true;
				tree[cnt].m.id = i;
				tree[cnt].sl = st[ed];
				tree[st[ed]].fa = cnt;
				st[ed] = cnt;
			} else {
				if (s[i] == 'x') {
					int sum = 0;
					for (i = i + 1; s[i] != ' '; i++) {
						sum = sum * 10 + s[i] - '0';
					}
					cnt++;
					w[sum] = cnt;
					tree[cnt].m.knd = false;
					tree[cnt].m.id = sum;
					ed++;
					st[ed] = cnt;
				}
			}
		}
		if (i == l - 1) {
			r = cnt;
		}
	}
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		int opt = 0;
		scanf("%d", &opt);
		tree[w[i]].vl = opt;
	}
	dfs(r);
	scanf("%d", &q);
	for (int i = 1; i <= q; i++) {
		int x = 0;
		scanf("%d", &x);
		tree[w[x]].vl = not tree[w[x]].vl;
		int t = w[x];
		while (t != r) {
			t = tree[t].fa;
			bool tp = tree[t].vl;
			if (s[tree[t].m.id] == '&') {
				tree[t].vl = tree[tree[t].sl].vl and tree[tree[t].sr].vl;
			} else {
				if (s[tree[t].m.id] == '|') {
					tree[t].vl = tree[tree[t].sl].vl or tree[tree[t].sr].vl;
				} else {
					tree[t].vl = not tree[tree[t].sl].vl;
				}
			}
			if (tp == tree[t].vl) {
				break;
			}
		}
		cout << tree[r].vl;
		tree[w[x]].vl = not tree[w[x]].vl;
		t = w[x];
		while (t != r) {
			t = tree[t].fa;
			bool tp = tree[t].vl;
			if (s[tree[t].m.id] == '&') {
				tree[t].vl = tree[tree[t].sl].vl and tree[tree[t].sr].vl;
			} else {
				if (s[tree[t].m.id] == '|') {
					tree[t].vl = tree[tree[t].sl].vl or tree[tree[t].sr].vl;
				} else {
					tree[t].vl = not tree[tree[t].sl].vl;
				}
			}
			if (tp == tree[t].vl) {
				break;
			}
		}
		printf("\n");
	}
	return 0;
}
inline void dfs(int node) {
	if (tree[node].m.knd == true) {
		if (s[tree[node].m.id] == '&') {
			dfs(tree[node].sl);
			dfs(tree[node].sr);
			tree[node].vl = tree[tree[node].sl].vl and tree[tree[node].sr].vl;
		} else {
			if (s[tree[node].m.id] == '|') {
				dfs(tree[node].sl);
				dfs(tree[node].sr);
				tree[node].vl = tree[tree[node].sl].vl or tree[tree[node].sr].vl;
			} else {
				if (s[tree[node].m.id] == '!') {
					dfs(tree[node].sl);
					tree[node].vl = not tree[tree[node].sl].vl;
				}
			}
		}
	}
}

这个代码或许有些长,但是跑得不慢。

如果有建议欢迎大家给建议。

希望大家不要直接把我代码直接交上去,还是要学会了自己写一遍,谢谢配合。

你可能感兴趣的:(算法,c++)