洛谷 P7324 - [WC2021] 表达式求值(状压+dp)
现场人傻系列……
首先建出 \(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)的更多相关文章
- P7324 [WC2021] 表达式求值
P7324 [WC2021] 表达式求值 闲话 WC2021 我只得了 20 分,三道题总共 20 分.我是下场了突然后知后觉这件事的,主要原因是我开了 C++11,然后 T1 T2 都没分了.在洛谷 ...
- 【题解】洛谷P2704 [NOI2001] 炮兵阵地(状压DP)
洛谷P2704:https://www.luogu.org/problemnew/show/P2704 思路 这道题一开始以为是什么基于状压的高端算法 没想到只是一道加了一行状态判断的状压DP而已 与 ...
- 【题解】洛谷P1896 [SCOI2005] 互不侵犯(状压DP)
洛谷P1896:https://www.luogu.org/problemnew/show/P1896 前言 这是一道状压DP的经典题 原来已经做过了 但是快要NOIP 复习一波 关于一些位运算的知识 ...
- 洛谷P1171 售货员的难题【状压DP】
题目描述 某乡有n个村庄(1 输入格式: 村庄数n和各村之间的路程(均是整数). 输出格式: 最短的路程. 输入样例: 3 0 2 1 1 0 2 2 1 0 输出样例 3 说明 输入解释 3 {村庄 ...
- 洛谷 P2622 关灯问题II【状压DP】
传送门:https://www.luogu.org/problemnew/show/P2622 题面: 题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的 ...
- UOJ #129 / BZOJ 4197 / 洛谷 P2150 - [NOI2015]寿司晚宴 (状压dp+数论+容斥)
题面传送门 题意: 你有一个集合 \(S={2,3,\dots,n}\) 你要选择两个集合 \(A\) 和 \(B\),满足: \(A \subseteq S\),\(B \subseteq S\), ...
- 2018.07.18 洛谷P1171 售货员的难题(状压dp)
传送门 感觉是一道经典的状压dp,随便写了一发卡了卡常数开了个O(2)" role="presentation" style="position: relati ...
- 洛谷P2761 软件补丁问题(状压dp)
传送门 啊咧……这题不是网络流二十四题么……为啥是个状压dp…… 把每一个漏洞看成一个状态,直接硬上状压dp 然后因为有后效型,得用spfa //minamoto #include<iostre ...
- 洛谷$P3226\ [HNOI2012]$集合选数 状压$dp$
正解:$dp$ 解题报告: 传送门$QwQ$ 考虑列一个横坐标为比值为2的等比数列,纵坐标为比值为3的等比数列的表格.发现每个数要选就等价于它的上下左右不能选. 于是就是个状压$dp$板子了$QwQ$ ...
随机推荐
- Framework - 性能统计
摘要 近期对接客户时,客户方希望提供 SDK 的性能.内存.隐私支持等一些数据,所以就对 SDK 进行了一些性能测试. 在用表格统计整理这些数据时,突然发现,经常用统计的方式看 SDK 的相关数据,似 ...
- Spring父子上下文的使用案例
Spring父子上下文的使用案例 一.背景 二.需求 三.实现步骤 1.基础代码编写 2.测试结果 四.小彩蛋 五.完整代码 一.背景 最近在看在使用Spring Cloud的时候发现,当我们通过Fe ...
- 『学了就忘』Linux基础 — 3、CentOS镜像下载
下载CentOS镜像可以从官网下载:https://www.centos.org/download/. 也可以从国内的镜像网站下载. 阿里云:https://mirrors.aliyun.com/ce ...
- Java并发:重入锁 ReentrantLock(二)
一.理解锁的实现原理 1. 用wait()去实现一个lock方法,wait()要和synchronized同步关键字一起去使用的,直接使用wait方法会直接报IllegalMonitorStateEx ...
- 利用Microsoft Graph开发微软的Exchange Calendar会议、Team
Microsoft Graph是什么? Microsoft Graph 是 Microsoft 365 中通往数据和智能的网关. 它提供统一的可编程模型,可用于访问 Microsoft 365.Win ...
- linux shell文件合并 去重 分割
1,合并+去重+分割 转载:shell 文件合并,去重,分割 - kakaisgood - 博客园 (cnblogs.com) 第一:两个文件的交集,并集前提条件:每个文件中不得有重复行1. 取出两个 ...
- hdu 5175 Misaki's Kiss again(GCD和异或)
题意: 给一个数N. 如果GCD(N,M) = N XOR M,则称M是一个kiss 1<=M<=N 问总共有多少个kiss.并且列出所有的值. 思路: 思路一:枚举M.有大量的GCD ...
- hdu 2190 重建希望小学(数学,递推)
题意: N*3的教室,有2种砖,2*2.1*1. 问铺设教室的方案有多少种.(要铺满) 思路: 画一下图可以很快发现递推公式 代码: int main(){ int a[35]; mem(a,0); ...
- Linux 文本三剑客之 grep
Linux 系统中一切皆文件. 文件是个文本.可以读.可以写,如果是二进制文件,还能执行. 在使用Linux的时候,大都是要和各式各样文件打交道.熟悉文本的读取.编辑.筛选就是linux系统管理员的必 ...
- (转)刚来的大神彻底干掉了代码中的if else...
一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性. 首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思.尤其是那些没有注释的代码. 其次 ...