【洛谷P7324】表达式求值

题目

题目链接:https://www.luogu.com.cn/problem/P7324
定义二元操作符 <:对于两个长度都为 (n) 的数组 (A, B)(下标从 (1)(n)),(A)<(B) 的结果也是一个长度为 (n) 的数组,记为 (C)。则有 (C[i] = min(A[i], B[i]))(1 le i le n))。
定义二元操作符 >:对于两个长度都为 (n) 的数组 (A, B)(下标从 (1)(n)),(A)>(B) 的结果也是一个长度为 (n) 的数组,记为 (C)。则有 (C[i] = max(A[i], B[i]))(1 le i le n))。
现在有 (m)(1 le m le 10))个长度均为 (n) 的整数数组 (A_0, A_1, ldots , A_{m-1})。给定一个待计算的表达式 (E),其满足 (E) 中出现的每个操作数都是 (A_0, A_1, ldots , A_{m-1}) 其中之一,且 (E) 中只包含 <>; 两种操作符(<> 的运算优先级相同),因此该表达式的结果值也将是一个长度为 (n) 的数组。
特殊地,表达式 (E) 中还可能出现操作符 ?,它表示该运算符可能是 < 也可能是 >。因此若表达式中有 (t)?,则该表达式可生成 (2^t) 个可求确定值的表达式,从而可以得到 (2^t) 个结果值,你的任务就是求出这 (2^t) 个结果值(每个结果都是一个数组)中所有的元素的和。你只需要给出所有元素之和对 ({10}^9 + 7) 取模后的值。
(n,|S|leq 5 imes 10^4;mleq 10)

思路

首先根据给出的表达式建立表达式树。
数组的每一位之间是互相独立的,所以考虑在表达式树上预处理出来所有情况,然后就可以枚举每一位计算答案。
但是如果直接枚举大小排列的话复杂度是 (O(m!|S|)),显然不可接受。
观察到本题只关心数字之间的大小关系,有一个经典的 trick 是考虑一个阈值 (k),把不超过 (k) 的设为 (0),超过 (k) 的设为 (1),然后进行完操作只需要看得到的是 (0) 还是 (1) 就可以判断答案与 (k) 的大小关系。
所以考虑设 (f[x][s][0/1]) 表示表达式树上点 (x) 为根的子树内,(m) 个表达式大小关系状态为 (s),且进行玩子树的表达式后答案在小的那部分 / 大的那部分的方案数。
其中一个状态 (s) 二进制下第 (i) 位表示第 (i) 个序列前这一位(上文提到每一个序列的每一位是独立的)是在小的部分 / 大的部分。
考虑转移,记左右儿子分别为 (lc,rc),拿当前节点的字符为 < 举例,有

[f[x][s][min(j,k)]gets f[lc][s][j] imes f[rc][s][k] (0leq j,kleq 1) ]

> 的话就把 (min) 改为 (max)? 就两个都要转移。

统计答案的部分就直接枚举每一位,然后把 (m) 个序列这一位的数字从小到大排序,依次枚举每一位,假设把这一位划进小的部分前的状态为 (S),划进去后状态为 (T),那么贡献即为

[val imes (f[rt][T][0]-f[rt][S][0]) ]

时间复杂度 (O(2^m|S|+nmlog m))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=50010,M=10,MOD=1e9+7;
int n,m,rt,len,ans,I,a[M][N],b[M],ch[N][2],f[N][1<<M][2];
char s[N];
stack<int> st;

bool cmp(int x,int y)
{
	return a[x][I]<a[y][I];
}

void build()
{
	for (int i=len;i>=1;i--)
	{
		if (s[i]!='(') st.push(i);
		if (s[i]=='(')
		{
			int x=st.top(); st.pop();
			while (s[st.top()]!=')')
			{
				int y=st.top(); st.pop();
				int z=st.top(); st.pop();
				ch[y][0]=z; ch[y][1]=x; x=y;
			}
			st.pop(); st.push(x);
		}
	}
	rt=st.top();
}

void dfs(int x)
{
	if (isdigit(s[x]))
	{
		int num=s[x]-48;
		for (int i=0;i<(1<<m);i++)
			f[x][i][(i>>num)&1]=1;
		return;
	}
	int lc=ch[x][0],rc=ch[x][1];
	dfs(lc); dfs(rc);
	for (int i=0;i<(1<<m);i++)
		for (int j=0;j<=1;j++)
			for (int k=0;k<=1;k++)
			{
				int sum=1LL*f[lc][i][j]*f[rc][i][k]%MOD;
				if (s[x]=='<' || s[x]=='?') f[x][i][min(j,k)]=(f[x][i][min(j,k)]+sum)%MOD;
				if (s[x]=='>' || s[x]=='?') f[x][i][max(j,k)]=(f[x][i][max(j,k)]+sum)%MOD;
			}
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=0;i<m;i++)
		for (int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	scanf("%s",s+2);
	len=strlen(s+2)+1;
	s[1]='('; s[++len]=')';
	build();
	dfs(rt);
	for (int i=0;i<m;i++) b[i]=i;
	for (int i=1;i<=n;i++)
	{
		I=i;
		sort(b,b+m,cmp);
		for (int j=0,S=(1<<m)-1;j<m;j++)
		{
			ans=(ans+1LL*(f[rt][S^(1<<b[j])][0]-f[rt][S][0])*a[b[j]][i])%MOD;
			S^=(1<<b[j]);
		}
	}
	printf("%d
",(ans+MOD)%MOD);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14810177.html