lingspace

题目大意

题目链接

对于 \(1\) 位的二进制变量定义两种运算 \(⊕\)\(\times\)

运算的优先级是:

  1. 先计算括号内的,再计算括号外的

  2. \(\times\) 运算优先于 \(⊕\) 运算

现在给出一个包含 \(L\) 个字符且仅含运算符号和括号的式子,例如 _+(_*_) ,输入时不含下划线,在 _ 处填上 \(0\)\(1\) ,试求最终一共有多少种方案使得式子最终的运算结果为 \(0\) 。由于结果可能很大,请输出方案数对 \(10007\) 取模后的结果。\(L \leq 10^5\)

解题思路

这是某次模拟赛的 \(B\) 题。当时看到题以为要转化成某种神秘的模型,一度想出了近似 \(O(nlogn)\) 的递归做法,后来事实证明这就是一个类 \(dp\) 算法,它作为一道表达式的题目,甚至不需要将中缀表达式转换成后缀表达式……有大佬考场曾经使用了上述算法,最终不幸爆炸(恶毒鹿:勿 \(cue\)

言归正传。这道题作为一道表达式求值的题目,自然能想到用 来处理中缀表达式。我们开一个栈,第一个记录到目前为止的所有待运算符号。接着用两个 类栈数组 分别记录可以使目前的运算结果为 \(0\)\(1\) 的方案总数。

处理式子时略微修改正常的中缀表达式求值代码就好。如果有不清楚如何求中缀表达式的同学,可以接着往下看,最后会有一遍总流程描述。

接着,我们考虑这个题目的性质。假如我们遇到了一个非括号的运算符号,那么我们应该考虑如何求出使它的运算结果为 \(0\)\(1\) 的方案总数。假设使倒数第二次运算结果为 \(0\) 的方案总数为 \(p\) ,为 \(1\) 的方案总数为 \(q\) ;使最后一次运算结果为 \(0\) 的方案总数为 \(x\) ,为 \(1\) 的方案总数为 \(y\)

分类讨论,假如遇到的是符号 \(⊕\) ,那么根据运算法则,要想令结果为 \(0\) ,两个操作数只能是 \(0\) 。根据乘法原理,此时结果为 \(0\) 的方案总数为 \(x \times p\) 。要想令结果为 \(1\) ,两个操作数可以分别是 \(0\)\(1\)\(1\)\(0\)\(1\)\(1\) 。所以此时的方案总数是 \(x \times q + y \times p + y \times q\)

假如遇到的是符号 \(\times\) 。根据运算法则,要想令结果为 \(0\) ,两个操作数可以是 \(0\)\(0\)\(0\)\(1\)\(1\)\(0\) ,所以方案总数是 \(x \times p + x \times q + y \times p\) 。要想令结果为 \(1\) ,两个操作数只能是 \(1\) ,所以方案总数是 \(y \times q\)

现在,我们已经知道了如何求出各种运算的方案总数。接下来,我们应该思考一些有关于题目的细节。我们不妨来模拟一遍处理符号的流程:

  1. 假如遇到了 \((\) ,它的优先级最高,所以我们应该直接直接把它压入符号栈,等待与 \()\) 的匹配。

  2. 假如遇到了 \(\times\) ,因为它的优先级次高,而左括号必须等待右括号与其匹配时才能出栈,所以直接把它压入符号栈。同时压入相应的操作数。

  3. 假如遇到了 \(⊕\) ,因为 \(\times\) 的优先级比它高,所以我们必须先把栈顶的所有 \(\times\) 处理完才能把它压入符号栈。同时压入相应的操作数。

  4. 假如遇到了 \()\) ,说明两个括号之间的运算符优先级最高,我们一直处理符号栈的栈顶,直到 \((\) 出栈。

接着考虑类栈数组的维护。考虑这样一个问题:\((\) 的左边和 \()\) 的右边是不能存在操作数的。所以当遇到非括号的运算符时,因为此时运算符刚刚入栈,只有在运算符出栈的时候才能进行运算。因此,我们可以把这种情况看作是没有运算符,所以结果为 \(0\)\(1\) 的方案总数分别只有 \(1\) 种,也就是它们本身。每次符号栈出栈时就从两个类栈数组中取出栈顶,按照上述的规则进行计算。

最后,如果符号栈中还存在符号,我们应该把这些符号依次处理,直到栈空为止。最后,类栈数组中只剩下了一个元素。我们把这个元素直接输出即可。

总而言之,这是一道细节比较繁琐的题目,不愧是普及组的压轴 \(dp\)。此题用中缀表达式转后缀表达式(逆波兰表达式)的做法也可以做,思路比较清晰,但是码量略大。在这里同时附上两份标程,在此特别感谢提供后缀表达式做法的 EDqwq 大佬!

参考代码

笔者标程

#include <cstdio>
using namespace std;

const int maxn = 1e5 + 5;
const int mod = 10007;

int n, cnt, tot;
int dp[maxn][2];
char c[maxn], a[maxn];

void solve(char c) {
	int x = dp[cnt][0], y = dp[cnt][1];
	int p = dp[cnt - 1][0], q = dp[cnt - 1][1];
	cnt--;
	if (c == \'+\') {
		dp[cnt][0] = (x * p) % mod;
		dp[cnt][1] = (x * q + y * p + y * q) % mod;
	} else if (c == \'*\') {
		dp[cnt][0] = (x * p + x * q + y * p) % mod;
		dp[cnt][1]= (y * q) % mod;
	}
}

int main() {
	scanf("%d", &n);
	scanf("%s", a);
	if (a[0] != \'(\') {
		dp[++cnt][0] = 1;
		dp[cnt][1] = 1;
	}
	for (int i = 0; i < n; i++) {
		if (a[i] == \'+\') {
			char t = c[tot];
			while (t == \'*\') {
				solve(t);
				tot--;
				t = c[tot];
			}
			c[++tot] = a[i];
			if (a[i + 1] != \'(\') {
				dp[++cnt][0] = 1;
				dp[cnt][1] = 1;
			}
		} else if (a[i] == \'*\') {
			c[++tot] = a[i];
			if (a[i + 1] != \'(\') {
				dp[++cnt][0] = 1;
				dp[cnt][1] = 1;
			}
		} else if (a[i] == \'(\') {
			c[++tot] = a[i];
			if (a[i + 1] != \'(\') {
				dp[++cnt][0] = 1;
				dp[cnt][1] = 1;
			}
		} else {
			char t = c[tot];
			while (t != \'(\') {
				solve(t);
				tot--;
				t = c[tot];
			}
			tot--;
		}
	}
	while (tot) {
		solve(c[tot]);
		tot--;
	}
	printf("%d\n", dp[1][0]);
	return 0;
}

后缀表达式

#include<bits/stdc++.h>

#define mem(x,y) memset(x,y,sizeof(x))
#define mod 10007

using namespace std;

int read() {
	int s = 0,w = 1;
	char ch = getchar();
	while(ch < \'0\' || ch > \'9\') {
		if(ch == \'-\')w = -1;
		ch = getchar();
	}
	while(ch >= \'0\' && ch <= \'9\')s = s * 10 + ch - \'0\',ch = getchar();
	return s * w;
}

int n;
string s;
string a;
int ans;

stack <char> q;

stack <int> q1;
stack <int> q2;

signed main() {
	a = "";
	cin>>n;
	cin>>s;
	s = \'#\' + s;
	for(int i = 1; i <= n; i ++) {
		if(q.empty()) {
			q.push(s[i]);
			if(s[i] != \'(\' && s[i] != \')\')a += \'%\';
			continue;
		}
		if(s[i] == \'(\' || s[i] == \'*\')q.push(s[i]);
		if(s[i] == \'+\') {
			while(!q.empty() && q.top() == \'*\')a += q.top(),q.pop();
			q.push(s[i]);
		}
		if(s[i] == \')\') {
			while(!q.empty() && q.top() != \'(\') {
				a += q.top();
				q.pop();
			}
			q.pop();
		}
		if(s[i] != \'(\' && s[i] != \')\')a += \'%\';
	}
	while(!q.empty())a += q.top(),q.pop();
	a = \'%\' + a;
	n = a.length();
	a = \'#\' + a;
	for(int i = 1; i <= n; i ++) {
		if(a[i] == \'%\')q1.push(1),q2.push(1);
		if(a[i] == \'*\') {
			int v1,v2;
			v1 = q1.top();
			v2 = q2.top();
			q1.pop();
			q2.pop();
			int v3,v4;
			v3 = q1.top();
			v4 = q2.top();
			q1.pop();
			q2.pop();
			q1.push((v1 * v4 + v1 * v3 + v2 * v3) % mod);
			q2.push(v2 * v4 % mod);
		}
		if(a[i] == \'+\') {
			int v1,v2;
			v1 = q1.top();
			v2 = q2.top();
			q1.pop();
			q2.pop();
			int v3,v4;
			v3 = q1.top();
			v4 = q2.top();
			q1.pop();
			q2.pop();
			q2.push((v1 * v4 + v2 * v4 + v2 * v3) % mod);
			q1.push(v1 * v3 % mod);
		}
	}
	while(!q1.empty())ans = q1.top(),q1.pop();
	cout<<ans;
}

分类:

技术点:

相关文章:

  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2017-12-16
  • 2021-08-15
猜你喜欢
  • 2021-09-25
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
  • 2021-08-04
相关资源
相似解决方案