题解:[CSP-J 2022] 逻辑表达式

洛谷P8815

Posted by TH911 on November 19, 2024

题目传送门

题意分析

给定一个 bool 运算表达式,求其值和两种布尔短路在运算途中发生的次数。

首先,对于这种让你“计算表达式”的题目,一般来讲就是栈、递归。

然而我们看到这之中,“运算优先级”就十分的烦人。

我们可以考虑手动给给定的表达式添加括号,也可以考虑转后缀或前缀表达式进行运算。

我们同样可以使用栈来模拟, & 直接算,而 | 在有括号时才计算。

在本文,给出一种基于递归的的做法。

处理策略

(不会有人不知道 $\and$ 是与,$\or$ 是或吧……)

对于给定表达式 $a$,我们一定可以将其拆分为若干表达式 $a[l_1,r_1],a[l_2,r_2],a[l_3,r_3],\cdots,a[l_p,r_p]$。

我们定义 $dfs(l,r)$ 表示 $a[l,r]$ 的值。

比如说对于表达式 1&(2|3),$a[1,7]$ 就是 1&(2|3),$a[4,6]$ 就是 2|3

对应地,$dfs(1,7)=dfs(1,1)\and dfs(4,6)=1\and(2\or3)=1$。

那么,我们考虑 $dfs(l,r)$ 内部怎么写。

首先,当 $l,r$ 满足 $l=r$ 时,无疑 $dfs(l,r)=a_l=a_r$。(无需考虑 $a_l$ 是不是符号,是符号的话不会调用)

记 $f_1[i],f_2[i]$ 分别为离 $a_i$ 最近的同一层括号内 |& 的位置。

那么当我们调用 $dfs(l,r)$ 时,先判断 $f_1[r],f_2[r]$ 的值是否在 $[l,r]$ 内,如果在,就说明需要进行计算。

以 $\large a_{f_1[r]}$ 为例:

我们将 $a[l,r]$ 拆分为两个表达式:$a[l,f_1[r]-1]\or a[f_1[r]+1,r]$,那么对应地就有递归式 $dfs(l,r)=dfs(l,f_1[r]-1)\or dfs(f_1[r]+1,r)$。

这样我们就可以递归求解。(其实就是形如 $a\and b$ 和 $a\or b$)。

如果 $f_1[r],f_2[r]$ 的值都不合法,两种情况:

  • $l=r$。

  • $a[l,r]$ 是形如 $(s)$ 的形式,其中 $s$ 为合法表达式;那么显然此时有 $dfs(l,r)=dfs(l+1,r-1)$。

    但是,注意不要将此条件放在 $dfs(l,r)$ 的开始的同时判定条件写为 if(a[l]=='('&&a[r]==')')。因为,$a[l,r]$ 同样也能形如 $(b)\and(c)$。

AC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const int N=1e6;
//1:|,2:& 
int n,cnt1,cnt2,pl1[N+1],pl2[N+1],f1[N+1],f2[N+1];
char a[N];
void pre(){
	static int pl1[N+1],pl2[N+1];
	int pl=0;
    for(int i=1;i<=n;i++){
    	switch(a[i]){
    		case '(':
    			pl++;
    			break;
    		case ')':
    			pl--;
    			break;
    		case '|':
    			pl1[pl]=i;
    			break;
    		case '&':
    			pl2[pl]=i;
    			break;
		}//i之前最近的 |和 &(同层括号内)
		f1[i]=pl1[pl];
		f2[i]=pl2[pl];
    }
}
bool dfs(int l,int r){
	if(l==r)return a[l]^48;
	//a|b
	if(f1[r]>l){
		bool ans=dfs(l,f1[r]-1);
		if(ans){
			cnt2++;
			return true;
		}return dfs(f1[r]+1,r);
	}//a&b
	else if(f2[r]>l){
		bool ans=dfs(l,f2[r]-1);
		if(!ans){
			cnt1++;
			return false;
		}return dfs(f2[r]+1,r);
	}//(a) -> a
	if(a[l]=='('&&a[r]==')')return dfs(l+1,r-1);
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
    scanf("%s",a+1);
    n=strlen(a+1);
	pre();
    int ans=dfs(1,n);
	printf("%d\n%d %d\n",ans,cnt1,cnt2);
    
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}