• 【洛谷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;
    }
    
  • 相关阅读:
    Java练习题
    循环练习
    Java String类详解
    Collections类
    TreeSet类
    IOS基础:NSTimer类的使用
    IOS基础:深入理解Objectivec中@class的含义
    IOS基础:retain,copy,assign及autorelease
    IOS基础:ActionSheet(上拉菜单)的实现
    Xcode 模拟器生成目标目录位置查找
  • 原文地址:https://www.cnblogs.com/stoorz/p/14810177.html
Copyright © 2020-2023  润新知