题面传送门

现场人傻系列……

首先建出 \(E\) 的表达式树,具体来说表达式的每一个叶子节点表示一个数组 \(A_i\),每一个非叶子节点都表示一次运算,它的值表示左右儿子进行该运算后得到的结果。这个可以通过建表达式树的套路方法在 \(\mathcal O(|E|)\) 的时间内求出来。

其次注意到数组每一位的贡献是独立的,也就是说我们可以对 \(n\) 位每一位都单独求一遍贡献。于是现在等价于我们解决 \(n\) 个如下的问题:

  • 给出一个数组 \(a_1,a_2,\cdots,a_m\) 和一个带问号的表达式 \(E\),其中 \(E\) 有些位置是 \(<\),有些位置是 \(>\),有些位置是 \(?\),你可以将 \(?\) 替换为 \(<\) 或 \(>\),求所有可能得到的表达式的值之和。

这个问题显然可以用树形 \(dp\) 求解,设 \(dp_{i,j}\) 表示执行完表达式树上 \(i\) 子树中所有运算后,得到的值为 \(a_j\) 的填法,转移就分该节点上的运算为 \(<\) 和 \(>\) 转移即可,相当于求一遍 \(\max/\min\) 卷积,时间复杂度 \(n|E|m^2\),当然如果想用前缀和优化可以做到 \(n|E|m\),反正都是暴力,过不去。

考虑优化,注意到 \(n\),也就是子问题个数很多,但是数组长度 \(m\) 非常非常小,并且每次运算的表达式都是一样的。我们考虑借鉴 CF878D 的套路,对每种可能的相对数组大小计算一遍贡献,不过直接枚举相对大小有 \(m!\) 种,再乘上表达式长度 \(5\times 10^4\),还是会 TLE。考虑对上面的子问题进行转化,对于一个子问题我们将这个子问题对应的 \(a\) 数组从小到大排个序,设为 \(b_1,b_2,\cdots,b_m\),显然最终任意一个表达式运算的结果一定出自这 \(m\) 个数,对于一个运算结果为 \(b_i\) 的表达式,我们不直接加上 \(b_i\) 的贡献,instead 我们用差分的思想,显然 \(\forall j\le i,b_i\ge b_j\),也就是说我们可以对于所有 \(i\) 统计运算得到的结果 \(\ge b_i\) 的表达式个数 \(c_i\),并令答案加上 \((b_i-b_{i-1})\times c_i\),不难发现这样算下来每种运算结果为 \(b_i\) 的表达式会产生 \((b_i-b_{i-1})+(b_{i-1}-b_{i-2})+\cdots+(b_1-b_0)=b_i\) 的贡献,符合题意。

我们考虑怎样统计运算得到的结果 \(\ge b_i\) 的表达式个数,我们回到原来的 \(a\) 数组,记 \(c_j=[a_j\ge b_i]\),对于一种表达式,我们将 \(c\) 数组带回到表达式进行运算,如果运算结果为 \(1\) 那么真正的运算结果 \(\ge b_i\),反之真正的运算结果 \(<b_i\),正确性显然。这样转化有什么好处呢?不难发现这样一转化,所有数都缩小到了 \([0,1]\) 范围内,可能的情况个数也就降到了 \(2^m\),于是我们对这 \(2^m\) 分别预处理一遍即可,即 \(f_{S}\) 为当 \(\forall x\in S,c_x=1,\forall x\notin S,c_x=0\) 时有多少个表达式运算得到的值为 \(1\),对于每个 \(S\) 显然可以在 \(\mathcal O(|E|)\) 内求出 \(f_S\),那么如果我们记 \(ST_i\) 为 \(\{j|a_j\ge b_i\}\),那么运算得到的结果 \(\ge b_i\) 的表达式个数为 \(f_{ST_i}\)。把 \(a\) 排个序后随便搞搞就好了,时间复杂度 \(|E|2^m+mn\log m\)。

const int MAXM=10;
const int MAXN=5e4;
const int MAXP=1<<10;
const int MOD=1e9+7;
int n,m,len,a[MAXM+5][MAXN+5],mch[MAXN+5];char s[MAXN+5];
int ch[MAXN*2+5][2],op[MAXN*2+5],ncnt=0,rt=0;
ll dp[MAXN*2+5][2],f[MAXP+5];pii p[MAXM+5];
int build(int l,int r){
int id=++ncnt;
if(l==r) return op[id]=s[l]-'0',id;
if(s[r]==')'){
if(mch[r]==l) id=build(l+1,r-1);
else{
op[id]=(s[mch[r]-1]=='<')?10:((s[mch[r]-1]=='>')?11:12);
ch[id][0]=build(l,mch[r]-2);
ch[id][1]=build(mch[r],r);
}
} else {
op[id]=(s[r-1]=='<')?10:((s[r-1]=='>')?11:12);
ch[id][0]=build(l,r-2);
ch[id][1]=build(r,r);
}
return id;
}
void dfs(int x,int st){
if(!ch[x][0]&&!ch[x][1]){dp[x][~st>>op[x]&1]=1;return;}
dfs(ch[x][0],st);dfs(ch[x][1],st);
if(op[x]!=10){
dp[x][0]=(dp[x][0]+1ll*dp[ch[x][0]][0]*dp[ch[x][1]][0])%MOD;
dp[x][1]=(dp[x][1]+1ll*dp[ch[x][0]][1]*dp[ch[x][1]][0]+1ll*dp[ch[x][0]][0]*dp[ch[x][1]][1]+1ll*dp[ch[x][0]][1]*dp[ch[x][1]][1])%MOD;
} if(op[x]!=11){
dp[x][1]=(dp[x][1]+1ll*dp[ch[x][0]][1]*dp[ch[x][1]][1])%MOD;
dp[x][0]=(dp[x][0]+1ll*dp[ch[x][0]][1]*dp[ch[x][1]][0]+1ll*dp[ch[x][0]][0]*dp[ch[x][1]][1]+1ll*dp[ch[x][0]][0]*dp[ch[x][1]][0])%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+1);len=strlen(s+1);stack<int> st;
for(int i=1;i<=len;i++){
if(s[i]=='(') st.push(i);
if(s[i]==')') mch[i]=st.top(),st.pop();
} rt=build(1,len);
for(int i=0;i<(1<<m);i++){
memset(dp,0,sizeof(dp));dfs(rt,i);
f[i]=dp[rt][1];//printf("%d %d\n",i,f[i]);
} int ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++) p[j]=mp(a[j][i],j);
sort(p,p+m);int msk=0;
for(int j=0;j<m;j++){
ans=(ans+1ll*f[msk]*(p[j].fi-p[j-1].fi))%MOD;
msk|=(1<<p[j].se);
}
} printf("%d\n",ans);
return 0;
}

洛谷 P7324 - [WC2021] 表达式求值(状压+dp)的更多相关文章

  1. P7324 [WC2021] 表达式求值

    P7324 [WC2021] 表达式求值 闲话 WC2021 我只得了 20 分,三道题总共 20 分.我是下场了突然后知后觉这件事的,主要原因是我开了 C++11,然后 T1 T2 都没分了.在洛谷 ...

  2. 【题解】洛谷P2704 [NOI2001] 炮兵阵地(状压DP)

    洛谷P2704:https://www.luogu.org/problemnew/show/P2704 思路 这道题一开始以为是什么基于状压的高端算法 没想到只是一道加了一行状态判断的状压DP而已 与 ...

  3. 【题解】洛谷P1896 [SCOI2005] 互不侵犯(状压DP)

    洛谷P1896:https://www.luogu.org/problemnew/show/P1896 前言 这是一道状压DP的经典题 原来已经做过了 但是快要NOIP 复习一波 关于一些位运算的知识 ...

  4. 洛谷P1171 售货员的难题【状压DP】

    题目描述 某乡有n个村庄(1 输入格式: 村庄数n和各村之间的路程(均是整数). 输出格式: 最短的路程. 输入样例: 3 0 2 1 1 0 2 2 1 0 输出样例 3 说明 输入解释 3 {村庄 ...

  5. 洛谷 P2622 关灯问题II【状压DP】

    传送门:https://www.luogu.org/problemnew/show/P2622 题面: 题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的 ...

  6. UOJ #129 / BZOJ 4197 / 洛谷 P2150 - [NOI2015]寿司晚宴 (状压dp+数论+容斥)

    题面传送门 题意: 你有一个集合 \(S={2,3,\dots,n}\) 你要选择两个集合 \(A\) 和 \(B\),满足: \(A \subseteq S\),\(B \subseteq S\), ...

  7. 2018.07.18 洛谷P1171 售货员的难题(状压dp)

    传送门 感觉是一道经典的状压dp,随便写了一发卡了卡常数开了个O(2)" role="presentation" style="position: relati ...

  8. 洛谷P2761 软件补丁问题(状压dp)

    传送门 啊咧……这题不是网络流二十四题么……为啥是个状压dp…… 把每一个漏洞看成一个状态,直接硬上状压dp 然后因为有后效型,得用spfa //minamoto #include<iostre ...

  9. 洛谷$P3226\ [HNOI2012]$集合选数 状压$dp$

    正解:$dp$ 解题报告: 传送门$QwQ$ 考虑列一个横坐标为比值为2的等比数列,纵坐标为比值为3的等比数列的表格.发现每个数要选就等价于它的上下左右不能选. 于是就是个状压$dp$板子了$QwQ$ ...

随机推荐

  1. STM32中操作寄存器GPIOB_CRL &= ~( 0x0F<< (4*0))与GPIOB_CRL &=~(0x0F)之间有什么区别吗?

    没有区别,作用相同.只是这样写便于修改和沿用. 对于只用到PB0端口的程序~(0x0f << (4*0)) 和~0x0f没有区别.0x0f <<(4*N) 就是 向左 移动N个 ...

  2. 【Java虚拟机8】自定义类加载器、类加载器命名空间、类的卸载

    前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起. 自定义类加载器 例1 一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的clas ...

  3. 【UE4 设计模式】简单工厂模式 Simple Factory Pattern

    概述 描述 又称为静态工厂方法 一般使用静态方法,根据参数的不同创建不同类的实例 套路 创建抽象产品类 : 创建具体产品类,继承抽象产品类: 创建工厂类,通过静态方法根据传入不同参数从而创建不同具体产 ...

  4. 联想SR658安装显卡驱动【NVIDIA Tesla V100】

    1. 安装基础依赖环境 yum -y install gcc kernel-devel kernel-headers 2.查看内核和源码版本是否一致 查看内核版本: ls /boot | grep v ...

  5. Noip模拟81 2021.10.20

    T1 语言 比较简单的题,然后就瞎写了,所以考场上就我一个写了线段树的,所以我的常数.... 所以就枚举动词的位置,找前面后面有没有出现$4$即可 1 #include<bits/stdc++. ...

  6. STM32中AD采样的三种方法分析

    在进行STM32F中AD采样的学习中,我们知道AD采样的方法有多种,按照逻辑程序处理有三种方式,一种是查询模式,一种是中断处理模式,一种是DMA模式.三种方法按照处理复杂方法DMA模式处理模式效率最高 ...

  7. CentOS8 部署 MySQL8

    前言 有来项目的数据库在此之前使用的是 Docker 部署的,具体可见文章 Docker 安装 MySQL8 ,服务器是阿里云 2C2G 的ECS轻量服务器,可能是配置问题有时不论查什么都要等很长很长 ...

  8. Treevalue(0x01)——功能概述

    TreeValue--一个通用树状数据结构与函数计算库 Treevalue v1.0.0版本已经于2021年10月24日正式发布,欢迎下载体验:opendilab / treevalue. 这算是tr ...

  9. amba web

    arm amba doc https://developer.arm.com/docs

  10. (转载)gcc -l参数和-L参数

    -l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so ...