#CSPJ202003. T3 表达式(AC代码)

2020 CSP_J 第二轮 第三题 

#CSPJ202003. T3 表达式(expr)

说明

小C热衷于学习数理逻辑。有一天,他发现了一种特别的逻辑表达式。在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为0或1,运算从左往右进行。

如果表达式中有括号,则先计算括号内的子表达式的值。特别的,这种表达式有且仅有以下几种运算:

1.与运算:𝑎 & 𝑏。当且仅当𝑎和𝑏的值都为1时,该表达式的值为1。其余情况该表达式的值为0。

2.或运算:𝑎 | 𝑏。当且仅当𝑎和𝑏的值都为0时,该表达式的值为0。其余情况该表达式的值为1。

3.取反运算:! 𝑎。当且仅当𝑎的值为0时,该表达式的值为1。其余情况该表达式的值为0。

小C想知道,给定一个逻辑表达式和其中每一个操作数的初始取值后,再取反某一个操作数的值时,原表达式的值为多少。

为了化简对表达式的处理,我们有如下约定:

表达式将采用后缀表达式的方式输入。后缀表达式的定义如下:

1.如果𝐸是一个操作数,则𝐸的后缀表达式是它本身。

2.如果𝐸是𝐸1𝑜𝑝 𝐸2形式的表达式,其中𝑜𝑝是任何二元操作符,且优先级不高于𝐸1、𝐸2中括号外的操作符,则𝐸的后缀式为𝐸1′𝐸2′𝑜𝑝,其中𝐸1′、𝐸2′分别为𝐸1、𝐸2的后缀式。

3.如果𝐸是(𝐸1)形式的表达式,则𝐸1的后缀式就是𝐸的后缀式。

同时为了方便,输入中:

a)与运算符(&)、或运算符(|)、取反运算符(!)的左右均有一个空格,但表达式末尾没有空格

b)操作数由小写字母𝑥与一个正整数拼接而成,正整数表示这个变量的下标。例如:x10,表示下标为10的变量x_{10}。数据保证每个变量在表达式中出现恰好一次

输入格式

第一行包含一个字符串𝑠,表示上文描述的表达式。

第二行包含一个正整数𝑛,表示表达式中变量的数量。表达式中变量的下标为1,2, … , 𝑛。

第三行包含𝑛个整数,第𝑖个整数表示变量x_i的初值。

第四行包含一个正整数𝑞,表示询问的个数。

接下来𝑞行,每行一个正整数,表示需要取反的变量的下标。注意,每一个询问的修改都是临时的,即之前询问中的修改不会对后续的询问造成影响。

数据保证输入的表达式合法。变量的初值为0或1。

输出格式

输出一共有𝑞行,每行一个0或1,表示该询问下表达式的值。

样例

输入数据 1

x1 x2 & x3 |
3
1 0 1
3
1
2
3

输出数据 1

1
1
0

样例

输入数据 2

x1 ! x2 x4 | x3 x5 ! & & ! &
5
0 1 0 1 1
3
1
3
5

输出数据 2

0
1
1

提示

【样例1解释】

该后缀表达式的中缀表达式形式为(x_1 & x_2) | x_3​。

对于第一次询问,将x_1的值取反。此时,三个操作数对应的赋值依次为0,0,1。原表达式的值为(0 & 0) | 1 = 1。

对于第二次询问,将x_2的值取反。此时,三个操作数对应的赋值依次为1,1,1。原表达式的值为(1 & 1) | 1 = 1。

对于第三次询问,将x_3的值取反。此时,三个操作数对应的赋值依次为1,0,0。原表达式的值为(1 & 0) | 0 = 0。

【样例2解释】

该表达式的中缀表达式形式为( ! x_1​) & (! (( x_2​|x_4) & ( x_3& (! x_5) ) ))。

【数据范围与提示】

对于20%的数据,表达式中有且仅有与运算(&)或者或运算(|)。

对于另外30%的数据,\left | s \right | \leqslant 1000,q \leqslant 1000,n \leqslant 1000

对于另外20%的数据,变量的初值全为0或全为1

对于100%的数据,1 \leqslant \left |s \right | \leqslant 1 \times 10^6,1 \leqslant q \leqslant 1 × 10^5,2 \leqslant n \leqslant 1 \times 10^5

其中,\left | s \right |表示字符串s的长度。


【题解】

暂无

【代码】

 

#include<bits/stdc++.h>
using namespace std;
string s;
int n;
int a[500010];
int sta[100010],p=0;
int zhi[500010],cnt;
int tree[500010][2]; 
int what[500010];
int c[500010];
int main(){
    //freopen("expr.in","r",stdin);
    //freopen("expr.out","w",stdout);
	getline(cin,s);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	cnt=n;
	for(int i=0;i<s.size();i++){
		if(s[i]=='x'){
			i++;
			int f=0;
			while(isdigit(s[i])){
				f=f*10+s[i]-'0';
				i++;
			} 
			sta[++p]=f;
			what[f]=0;
		}else if(s[i]=='&'){
			what[++cnt]=1;
			tree[cnt][1]=sta[p--];
			tree[cnt][0]=sta[p--];
			a[cnt]=a[tree[cnt][0]]&a[tree[cnt][1]];
			sta[++p]=cnt;
		}else if(s[i]=='|'){
			what[++cnt]=2;
			tree[cnt][1]=sta[p--];
			tree[cnt][0]=sta[p--];
			a[cnt]=a[tree[cnt][0]]|a[tree[cnt][1]];
			sta[++p]=cnt;
		}else if(s[i]=='!'){
			what[++cnt]=3;
			tree[cnt][0]=sta[p--];
			a[cnt]=!a[tree[cnt][0]];
			sta[++p]=cnt;
		}
	}
	c[cnt]=1;
	for(int i=cnt;i>n;i--){
		int l=tree[i][0],r=tree[i][1];
		if(what[i]==1){
			if(((!a[l])&a[r])!=a[i])c[l]=c[i];
			if((a[l]&(!a[r]))!=a[i])c[r]=c[i]; 
		}else if(what[i]==2){
			if(((!a[l])|a[r])!=a[i])c[l]=c[i];
			if((a[l]|(!a[r]))!=a[i])c[r]=c[i]; 
		}else if(what[i]==3){
			c[l]=c[i]; 
		}
	}
	int q;
	scanf("%d",&q);
	while(q--){
		int k;
		scanf("%d",&k);
		printf("%d\n",a[cnt]^c[k]);
	} 
	return 0;
}

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 题目描述: 给定一个逻辑表达式,包含变量和运算符,其中变量用大写字母表示,运算符包括与(&)、或(|)、非(!)和异或(^),请输出该表达式的真值。 输入格式: 输入共一行,为一个字符串,表示逻辑表达式,字符串长度不超过 100。 输出格式: 输出共一行,为一个整数,表示该表达式的真值,1 表示真, 表示假。 输入样例: A&B&C|!D^E 输出样例: 1 解题思路: 本题可以使用栈来解决,具体思路如下: 1.首先定义两个栈,一个用来存储运算符,一个用来存储操作数。 2.遍历表达式中的每一个字符,如果是运算符,则将其压入运算符栈中,如果是操作数,则将其压入操作数栈中。 3.当遇到一个右括号时,从操作数栈中弹出两个操作数,从运算符栈中弹出一个运算符,进行运算,并将结果压入操作数栈中。 4.最终操作数栈中只剩下一个元素,即为表达式的真值。 代码实现: ### 回答2: 题目链接:https://www.luogu.com.cn/problem/CSP-J-2022 本题需要我们求得不含变量 `x` 的逻辑表达式的真值表。 首先分析题目中给出的逻辑运算符:`NOT`、`AND`、`OR`、`XOR`。这些逻辑运算符在计算机科学中很常见,它们分别表示取反、且、或、异或的运算。我们可以通过一个简单的真值表来表示它们的运算规则: |p|q|NOT p|p AND q|p OR q|p XOR q| |-|-|-----|------|------|-------| |0|0| 1 | 0 | 0 | 0 | |0|1| 1 | 0 | 1 | 1 | |1|0| 0 | 0 | 1 | 1 | |1|1| 0 | 1 | 1 | 0 | 通过这个真值表,我们可以很容易地推导出不同逻辑表达式的结果。例如,当 `p` 为真且 `q` 为假时,`p AND q` 的结果就是假;`p OR q` 的结果就是真。同理,当 `p` 和 `q` 相同的时候,它们的异或结果就是假,否则就是真。 而本题中的逻辑表达式是由多个运算符和 `x` 变量组成的,我们需要计算出真值表。为了避免直接枚举每一种情况,并逐个计算结果,我们可以采用二叉树的思路,即每个节点代表一个逻辑运算符,左右子树分别为该运算符的两个参数,直到叶节点为止,叶节点即为一个变量(可能是 `x` 或者是常量)。 具体操作方法可以采用递归实现。递归函数需要传入一个字符串表达式,返回该表达式是真还是假。具体实现细节可见代码。最终我们可以得到一个 1 行 2 的 n 次方 列的真值表。 代码: ### 回答3: 题目描述 给定 $m$ 个逻辑变量 $a_1, a_2, \cdots, a_m$,以及 $k$ 个逻辑表达式 $f_1, f_2, \cdots, f_k$,每个逻辑表达式都是由 $m$ 个逻辑变量和 $\land, \lor, \oplus, \neg$ 构成的合法逻辑表达式。 定义 $\operatorname{sat}(S)$ 为集合 $S$ 中的逻辑变量对应的真值赋值下,存在至少一个使得所有的逻辑表达式都为真。 例如,对于逻辑变量 $a, b, c$ 和两个逻辑表达式 $(a \lor b)$ 和 $(b \Rightarrow \neg c)$,$\operatorname{sat}(\{a=\mathrm{true}, b=\mathrm{false}, c=\mathrm{true}\})= false$。 给定一个逻辑表达式 $f$,定义其等效表示为 $\operatorname{simpl}(f)$。具体过程如下: - 根据与运算和或运算的结合律联想律,将逻辑表达式化为形如 $\ell_1 \land \ell_2 \land \cdots \land \ell_p$ 的简单合取范式,其中每个 $\ell_i$ 都是形如 $x_1 \lor x_2 \lor \cdots \lor x_q$ 的简单析取项; - 对于每个简单析取项 $x_1 \lor x_2 \lor \cdots \lor x_q$,在线性时间内计算一个包含了 $S$ 中逻辑变量对应的所有真值赋值中满足其为真的赋值的指示函数 $\phi_{x_1 \lor x_2 \lor \cdots \lor x_q}$。具体地,使用一个变量 $f=\mathrm{false}$ 并进行以下操作 $q$ 次:若 $S$ 中某个逻辑变量对应的值可以使得 $x_i$ 为 true,则让 $f$ 取为 $\mathrm{true}$,最终令 $\phi_{x_1 \lor x_2 \lor \cdots \lor x_q}=f$; - 最终得到的 $\operatorname{simpl}(f)$ 是 $\ell_1', \ell_2', \cdots, \ell_p'$ 的形式,其中每个 $\ell_i'$ 是二元组 $(\phi_{x_{i,1}}, \phi_{x_{i,2}})$,表示存在一组完全赋值 $v$ 使得 $\operatorname{sat}(\{a_i=v_i\})=\mathrm{true}$ 当且仅当 $x_{i,1}(v) \lor x_{i,2}(v)=\mathrm{true}$。 例如,对于逻辑表达式 $(a \land b) \Rightarrow ((a \lor b) \land (\neg a \lor \neg b))$,其 $\operatorname{simpl}$ 值为 $((a, b), ((a,b), (\neg a, \neg b)))$。 现在,请你构造一个逻辑表达式 $g$,使得 $\operatorname{simpl}(g)$ 中只有 $O(k+2^m)$ 个二元组,且对于所有的 $S$,$\operatorname{sat}(S)$ 在 $\operatorname{simpl}(g)$ 中的判定时间复杂度为 $O(k)$。 数据范围 对于所有测试点,保证 $1 \leqslant m \leqslant 10, 1 \leqslant k \leqslant 3m$。 本题满分为 $100$ 分。对于在此公开测试数据中得分为 $0$ 分的提交,我们将根据提交的相似度情况视情况给出提示,请注意查看提交结果页面。 算法1 (打表找规律) $O(m 2^{3m})$ 我们需要解决两个问题:如何根据 $\operatorname{simpl}(f)$ 判断 $\operatorname{sat}(S)$ 是否为 $\mathrm{true}$,以及如何构造出一个表达式 $g$,使得对于所有 $S$ 都有 $\operatorname{simpl}(g)$ 可以在 $O(k)$ 时间内判断 $\operatorname{sat}(S)$ 是否为 $\mathrm{true}$。 先考虑第一个问题。容易发现,对于一个二元组 $(\phi_{x_1}, \phi_{x_2})$,如果 $\operatorname{simpl}(f)$ 中存在一个简单析取项 $x_1 \lor x_2$,且 $\phi_{x_1}$ 和 $\phi_{x_2}$ 均为真函数,则 $\operatorname{simpl}(f)$ 在 $S$ 中的真值即为 $\mathrm{true}$。注意到 $\phi_{x_1}, \phi_{x_2}$ 都是只由 $S$ 中的逻辑变量决定的 01 函数,因此我们可以使用一个 $3^m \times 3^m$ 的矩阵 $mat_x$ 表示 $x$ 对 $S$ 的真值的影响。$mat_x(i,j)=1$ 表示当 $x$ 中逻辑变量取值的指示函数为 $i$ 和 $j$ 时,$\operatorname{sat}(S)$ 是否为 $\mathrm{true}$。注意到 $x$ 有 $2^m$ 种取值,因此我们需要计算 $O(2^m)$ 个矩阵 $mat_x$,每个矩阵的计算可以使用一组对应的二元组 $(\phi_{x_1}, \phi_{x_2})$ 和矩阵加法计算得到。接着我们可以把 $\operatorname{simpl}(f)$ 中所有的简单析取项的对应矩阵加起来,最终得到 $k$ 个 $3^m \times 3^m$ 的矩阵 $mat_1, mat_2, \cdots, mat_k$。判断 $\operatorname{simpl}(f)$ 在 $S$ 中的真值即可以通过计算 $\prod_{i=1}^k mat_i$ 得到。 接着考虑第二个问题。我们需要构造出一个表达式 $g$,使得 $\operatorname{simpl}(g)$ 在 $O(k)$ 时间内能判断 $\operatorname{sat}(S)$ 是否为 $\mathrm{true}$。首先考虑简单情况即 $m=1$。设 $a$ 为 $S$ 中唯一的变量。如果 $k=1$,令 $g=a$ 即可;如果 $k=2$,令 $g=\neg a \land f_2 \lor a\land \neg f_2$ 即可;如果 $k=3$,令 $g=\neg a (\neg f_2 \land \neg f_3) \lor a(\neg f_2 \land f_3) \lor f_2 \land f_3$ 即可;如果 $k=4$,令 $g=a(\neg f_2 \land f_4) \lor \neg a(f_3 \land f_4)$ 即可。可发现,对于给定的 $m,k$,这些表达式都不超过常数个。接着考虑如何推广到一般情况。 设 $a_1, a_2, \cdots, a_m$ 为 $S$ 中的 $m$ 个变量。考虑令 $g_i$ 表示 $S$ 中变量 $a_i$ 的值($0$ 或 $1$)。首先考虑对一个简单析取项 $x_1 \lor x_2 \lor \cdots \lor x_q$ 进行处理。对于 $i \in [1,q]$,设 $x_i$ 的指示函数为 $(\phi_{x_i,1}, \phi_{x_i,2})$,考虑令 $y_i$ 表示“至少有一个 $x_1, x_2, \cdots, x_i$ 在 $S$ 中是真的”,即 $y_1=x_1, y_i=y_{i-1} \lor x_i$。注意到 $y_i$ 极易通过 $\mathrm{or}$ 的结合律计算得到,因此我们可以直接把所有的二元组加入表达式 $g$ 中。此外,为了判断 $\operatorname{sat}(S)$ 是否为 $\mathrm{true}$,我们只需要计算 $y_q$ 即可。 接着考虑如何计算 $g_i$。我们构建一个由 $2^m$ 个节点组成的满二叉树,每个节点代表一个赋值 $v_1, v_2, \cdots, v_m$。根节点对应所有变量的值都是未定的赋值。为了方便起见,我们设根节点所在的深度为 $1$(节点深度定义为从根节点到该节点的从上到下路径上穿过的边数目加一)。除了根节点外,每个节点 $u$ 都对应一个深度为 $i_u$ 的变量 $a_{u, i_u}$ 的值。仅当 $a_{u, i_u}$ 的值已经被确定时,才可以计算 $g_{i_u}(u)$(为了防止重复计算,不考虑已经被计算过的 $g$ 值)。$g_{i_u}(u)$ 的值也是二元组 $(\phi_{x_{i_u,1}}, \phi_{x_{i_u,2}})$ 的形式,表示当前 $\operatorname{sat}(S)$ 在 $u$ 对应的赋值下为真的充要条件为 $\phi_{x_{i_u,1}}(u)=\mathrm{true}$ 或 $\phi_{x_{i_u,2}}(u)=\mathrm{true}$。这些值可以通过矩阵乘法计算得到(注意到构成 $g_{i_u}(u)$ 的所有简单析取项的矩阵已经计算得到)。最后根据 $g_1(\mathrm{root}) \lor g_2(\mathrm{root}) \lor \cdots \lor g_m(\mathrm{root})$ 判断 $\operatorname{sat}(S)$ 是否为 $\mathrm{true}$ 即可。 时间复杂度 计算 $\operatorname{simpl}(f)$ 中所有 二元组需 $O(k \cdot 2^m \cdot 3^{2m})$ 的时间,处理 $g$ 的过程中遍历了一个深度为 $m+1$ 的二叉树,节点数为 $2^{m+1}-1$。每个节点计算 $g_{i_u}(u)$ 的时间复杂度为 $O(2^{3m})$,因此总时间复杂度为 $O(k \cdot 2^m \cdot 3^{2m} + (2^{m+1}-1)2^{3m}) \leqslant O(m 2^{3m})$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值