【洛古 P8815】[CSP-J 2022] 逻辑表达式

5 篇文章 0 订阅
3 篇文章 0 订阅

[CSP-J 2022] 逻辑表达式

题目描述

逻辑表达式是计算机科学中的重要概念和工具,包含逻辑值、逻辑运算、逻辑运算优先级等内容。

在一个逻辑表达式中,元素的值只有两种可能: 0 0 0(表示假)和 1 1 1(表示真)。元素之间有多种可能的逻辑运算,本题中只需考虑如下两种:“与”(符号为 &)和“或”(符号为 |)。其运算规则如下:

0 & 0 = 0 & 1 = 1 & 0 = 0 0 \mathbin{\&} 0 = 0 \mathbin{\&} 1 = 1 \mathbin{\&} 0 = 0 0&0=0&1=1&0=0 1 & 1 = 1 1 \mathbin{\&} 1 = 1 1&1=1
0 ∣ 0 = 0 0 \mathbin{|} 0 = 0 00=0 0 ∣ 1 = 1 ∣ 0 = 1 ∣ 1 = 1 0 \mathbin{|} 1 = 1 \mathbin{|} 0 = 1 \mathbin{|} 1 = 1 01=10=11=1

在一个逻辑表达式中还可能有括号。规定在运算时,括号内的部分先运算;两种运算并列时,& 运算优先于 | 运算;同种运算并列时,从左向右运算。

比如,表达式 0|1&0 的运算顺序等同于 0|(1&0);表达式 0&1&0|1 的运算顺序等同于 ((0&1)&0)|1

此外,在 C++ 等语言的有些编译器中,对逻辑表达式的计算会采用一种“短路”的策略:在形如 a&b 的逻辑表达式中,会先计算 a 部分的值,如果 a = 0 a = 0 a=0,那么整个逻辑表达式的值就一定为 0 0 0,故无需再计算 b 部分的值;同理,在形如 a|b 的逻辑表达式中,会先计算 a 部分的值,如果 a = 1 a = 1 a=1,那么整个逻辑表达式的值就一定为 1 1 1,无需再计算 b 部分的值。

现在给你一个逻辑表达式,你需要计算出它的值,并且统计出在计算过程中,两种类型的“短路”各出现了多少次。需要注意的是,如果某处“短路”包含在更外层被“短路”的部分内则不被统计,如表达式 1|(0&1) 中,尽管 0&1 是一处“短路”,但由于外层的 1|(0&1) 本身就是一处“短路”,无需再计算 0&1 部分的值,因此不应当把这里的 0&1 计入一处“短路”。

输入格式

输入共一行,一个非空字符串 s s s 表示待计算的逻辑表达式。

输出格式

输出共两行,第一行输出一个字符 01,表示这个逻辑表达式的值;第二行输出两个非负整数,分别表示计算上述逻辑表达式的过程中,形如 a&ba|b 的“短路”各出现了多少次。

样例 #1

样例输入 #1

0&(1|0)|(1|1|1&0)

样例输出 #1

1
1 2

样例 #2

样例输入 #2

(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0

样例输出 #2

0
2 3

提示

【样例解释 #1】

该逻辑表达式的计算过程如下,每一行的注释表示上一行计算的过程:

0&(1|0)|(1|1|1&0)
=(0&(1|0))|((1|1)|(1&0)) //用括号标明计算顺序
=0|((1|1)|(1&0))   //先计算最左侧的 &,是一次形如 a&b 的“短路”
=0|(1|(1&0))       //再计算中间的 |,是一次形如 a|b 的“短路”
=0|1               //再计算中间的 |,是一次形如 a|b 的“短路”
=1

【样例 #3】

见附件中的 expr/expr3.inexpr/expr3.ans

【样例 #4】

见附件中的 expr/expr4.inexpr/expr4.ans

【数据范围】

∣ s ∣ \lvert s \rvert s 为字符串 s s s 的长度。

对于所有数据, 1 ≤ ∣ s ∣ ≤ 10 6 1 \le \lvert s \rvert \le {10}^6 1s106。保证 s s s 中仅含有字符 01&|() 且是一个符合规范的逻辑表达式。保证输入字符串的开头、中间和结尾均无额外的空格。保证 s s s
中没有重复的括号嵌套(即没有形如 ((a)) 形式的子串,其中 a 是符合规范的逻辑表
达式)。

测试点编号 ∣ s ∣ ≤ \lvert s \rvert \le s特殊条件
1 ∼ 2 1 \sim 2 12 3 3 3
3 ∼ 4 3 \sim 4 34 5 5 5
5 5 5 2000 2000 20001
6 6 6 2000 2000 20002
7 7 7 2000 2000 20003
8 ∼ 10 8 \sim 10 810 2000 2000 2000
11 ∼ 12 11 \sim 12 1112 10 6 {10}^6 1061
13 ∼ 14 13 \sim 14 1314 10 6 {10}^6 1062
15 ∼ 17 15 \sim 17 1517 10 6 {10}^6 1063
18 ∼ 20 18 \sim 20 1820 10 6 {10}^6 106

其中:
特殊性质 1 为:保证 s s s 中没有字符 &
特殊性质 2 为:保证 s s s 中没有字符 |
特殊性质 3 为:保证 s s s 中没有字符 ()

【提示】

以下给出一个“符合规范的逻辑表达式”的形式化定义:

  • 字符串 01 是符合规范的;
  • 如果字符串 s 是符合规范的,且 s 不是形如 (t) 的字符串(其中 t 是符合规范的),那么字符串 (s) 也是符合规范的;
  • 如果字符串 ab 均是符合规范的,那么字符串 a&ba|b 均是符合规范的;
  • 所有符合规范的逻辑表达式均可由以上方法生成。

解题思路

参考文章 表达式的转换我们可以把输入的逻辑表达式转换成后缀表达式。设l1,l2表示共发生l1个‘&’ 短路,l2个‘|’短路.

然后在计算的时候也开两个标记a,b,表示得到每个计算结果通过了a个‘&’ 短路,b个‘|’短路,

分类讨论

  • 当前计算为 p 1 p1 p1& p 2 = p p2=p p2=p时:

    1. 若p1==0,发生‘&’ 短路,p2中的短路不算了,p继承p1中的短路, p . a = p 1. a + 1 , p . b = p 1. b p.a=p1.a+1,p.b=p1.b p.a=p1.a+1,p.b=p1.b
    2. 对于总短路l1,l2,要把p2中发生的短路减去 l 1 − = p 2. a , l 2 − = p 2. b ; l1-=p2.a,l2-=p2.b; l1=p2.a,l2=p2.b;
  • 当前计算为 p 1 p1 p1| p 2 = p p2=p p2=p时:

    1. 若p1==1,发生‘|’ 短路,p2中的短路不算了,p继承p1中的短路, p . a = p 1. a + 1 , p . b = p 1. b p.a=p1.a+1,p.b=p1.b p.a=p1.a+1,p.b=p1.b
    2. 对于总短路l1,l2,要把p2中发生的短路减去 l 1 − = p 2. a , l 2 − = p 2. b ; l1-=p2.a,l2-=p2.b; l1=p2.a,l2=p2.b;

代码
//代码中的p1,p2与解题思路中的相反

#include<bits/stdc++.h>
#define ll long long

using namespace std;
char a[1100000];
int l,l1,l2;

struct cc{
	int a,b;
};

pair<int,cc>p1,p2,p;
stack<int>num;
stack<char>dat;
stack<char>op;
stack<pair<int,cc> >dat2;

int check(char c){
	switch(c){
		case ')':return 0;
		case '(':return 0;
		case '|':return 1;
		case '&':return 2;
		default: return -1;	
	}
}

void js(char c){
	cc w;
	p1=dat2.top();
	dat2.pop();
	p2=dat2.top();
	dat2.pop();
	if(c=='&')
	{
		p.first=p1.first&p2.first;
		p.second.a=p.second.a;
		p.second.b=0;	
		if(p2.first==0)
		{
			l1++;
			p.second.a=p2.second.a+1;
			p.second.b=p2.second.b;
			l1-=p1.second.a,l2-=p1.second.b;	
		}
		else
		{
			p.second.a=p1.second.a+p2.second.a;
			p.second.b=p1.second.b+p2.second.b;
		}
	}	
	if(c=='|')
	{
		p.first=p1.first|p2.first;	
	
		if(p2.first==1)
		{
			l2++;
			p.second.a=p2.second.a;
			p.second.b=p2.second.b+1;	
			l1-=p1.second.a,l2-=p1.second.b;
		}
		else
		{
			p.second.a=p1.second.a+p2.second.a;
			p.second.b=p1.second.b+p2.second.b;
		}
		
	}
}
void work2(){
	while(!dat.empty())
	{
		char t=dat.top();
	
		dat.pop();
		op.push(t);
	}
	while(!op.empty())
	{
		char c=op.top();	
		if(c>='0'&&c<='9')
		{
			cc x;
			x.a=0,x.b=0;
			dat2.push(make_pair(c-'0',x));
			op.pop();
		}
		else {
			js(c);
			dat2.push(p);	
			op.pop();
		}
	}
	printf("%d\n",dat2.top());
	printf("%d %d",l1,l2);
}

void work1(){
	for(int i=1;i<=l;i++)
	{
		if(a[i]>='0'&&a[i]<='9')
			dat.push(a[i]);
		else 
		{
			if(op.empty())
			{
				op.push(a[i]);
				continue;
			}
			char c=op.top();
			if(a[i]=='(')
			{	op.push(a[i]);
				continue;
			}
			if(a[i]==')')
			{
				while(c!='('&&!op.empty())
				{
					dat.push(c);
					op.pop();	
					if(!op.empty())	
						c=op.top();
				}
				op.pop();
				continue;
			}
			if(check(a[i])>check(c))
			{
				op.push(a[i]);
			}
			else{
				while(check(a[i])<=check(c)&&!op.empty())
				{
					dat.push(c);
					op.pop();	
					if(!op.empty())	
						c=op.top();
				}
				op.push(a[i]);
			}
		}
	}
	while(!op.empty())
	{
		dat.push(op.top());	
		op.pop();
	}
}

int main(){
	scanf("%s",a+1);
	l=strlen(a+1);
	work1();
	work2();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值