把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P7324 [WC2021] 表达式求值

题面传送门
考试的时候脑子抽了,想到\(70\)分暴力就没想了,甚至连状压都想到了,然后弃了。因为我第一题动都没动。
现在想想真是血亏。
首先发现每一位是没有任何关系的,所以可以每一位分开考虑。
这个式子首先肯定是先搞一个栈转成后缀形式再搞就比较好搞。
没有问号直接扫一遍求值即可。
考虑有问号的直接上\(dp\)迭代考虑。
发现如果直接在\(dp\)中算答案很难算,考虑变成计数\(dp\)
\(dp_{i,j}\)表示迭代到了\(i\)位,当前答案是第\(j\)个的方案数。
注意这个当前是指左边第一个左括号到现在的答案。
这样的话需要在每个右括号枚举前面两位转移,时间复杂度\(O(n|E|m^2)\),有\(55\)分,实现好一点有\(70\)分。
考虑怎么优化。
其实如果我们确定了要计算答案的数,剩余的数只有\(0/1\)两种形态,所以这个就可以优化了。
\(dp_{i,0/1}\)表示到了\(i\)位,当前值小于想要算的值或大于想要算的值的方案数,最后答案经典差分一下即可。
这个东西是\(O(n|E|m)\),在少爷机上似乎能跑过\(70\)分。
这个东西看着似乎已经到顶了,很难优化。
但是这道题的\(m\)特别小。似乎可以状压。
注意到对于每个值在计算答案时基本都是重复的,所以可以预处理一下就好了。
时间复杂度\(O(2^m|E|+nmlogn)\)
代码实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,sh,a[50039][13],st[50039],b[13],flag[13],now,pus,last;
unsigned long long dp[50039][2],ans,tot[10039];
char s[50039],_s;
inline bool cmp(int x,int y){return b[x]<b[y];}
int main(){
//	freopen("expr.in","r",stdin);
	//freopen("expr.out","w",stdout);
	register int i,j,h;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){
		for(j=1;j<=n;j++)scanf("%d",&a[j][i]);
	}
	scanf("%s",s+1);k=strlen(s+1);
	for(i=(1<<m)-1;i>=0;i--){
		memset(dp,0,sizeof(dp));sh=0;
		for(j=1;j<=m;j++)b[j]=(i>>j-1)&1;
		for(j=1;j<=k;j++){
			if(s[j]>='0'&&s[j]<='9') {
				now=s[j]-'0'+1;
				if(st[sh]=='('||!sh)dp[j][b[now]]=1;
				else{
				    last=st[sh-1]-200;
				    if(st[sh]!='>')(b[now])?(dp[j][0]=dp[last][0],dp[j][1]=dp[last][1]):(dp[j][0]=dp[last][1]+dp[last][0]);
				    if(st[sh]!='<')(!b[now])?(dp[j][0]+=dp[last][0],dp[j][1]+=dp[last][1]):(dp[j][1]+=dp[last][1]+dp[last][0]);
				}
				st[++sh]=j+200;
			}
			else{
				if(s[j]==')') {
					while(st[sh]!='(') sh--;sh--;last=st[sh-1]-200;_s=st[sh];
					if(_s!='<') dp[j][0]=dp[last][0]*dp[j-1][0],dp[j][1]=dp[last][1]*dp[j-1][0]+dp[j-1][1]*dp[last][0]+dp[last][1]*dp[j-1][1];
					if(_s!='>') dp[j][1]+=dp[j-1][1]*dp[last][1],dp[j][0]+=dp[j-1][0]*dp[last][0]+dp[j-1][1]*dp[last][0]+dp[j-1][0]*dp[last][1]; 
					st[++sh]=j+200;
				}
				else st[++sh]=s[j];
			}
			dp[j][0]%=mod;dp[j][1]%=mod;
		}
		tot[i]=dp[k][1];
	} 
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++) flag[j]=j,b[j]=a[i][j];
		sort(flag+1,flag+m+1,cmp);now=0;
		for(j=last=m;j;j=--last){
			while(b[flag[j]]==b[flag[last]])now|=1<<flag[last--]-1;last++;
			ans=(ans+tot[now]*(b[flag[last]]-b[flag[last-1]]))%mod;
		}
	}
	printf("%llu\n",ans);
}
posted @ 2021-02-11 16:12  275307894a  阅读(63)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end