P7324 [WC2021] 表达式求值
P7324 [WC2021] 表达式求值
闲话
WC2021 我只得了 20 分,三道题总共 20 分。我是下场了突然后知后觉这件事的,主要原因是我开了 C++11,然后 T1 T2 都没分了。在洛谷上测本来能拿银牌的。T1 的乱搞能拿 48,还挺高的。
幸亏咱们陕西省选不看冬令营成绩。幸亏是在省选前犯的这个错误。告诫后人和自己,写题前一定要看编译选项,否则只能后悔莫及。
T2 场上写的是不带问号的 \(O(n|E|)\) 和带问号 \(O(n|E|m^2)\) 的 70 分暴力。后者可以用 minmax 卷积优化掉一个 \(m\) 获得 85 分的好成绩。
思路
首先要会上面提到的带问号的暴力,不需要优化。
我在场上写的是这样子的:
int a[maxn][10],but[maxn][10],tmp[10];
int* solve(int l,const int &r,const int *a){
l++;
int *ans=s[l]=='('?solve(l,to[l],a):but[l];//这里的 to 是预处理的括号匹配位置,完全可以用把 l 设成全局变量的方法解决
l=s[l]=='('?to[l]+1:l+1;
while(l+1<r){
char op=s[l++];
int *res=s[l]=='('?solve(l,to[l],a):but[l];
l=s[l]=='('?to[l]+1:l+1;
switch(op){
case '<':{
memset(tmp,0,sizeof tmp);
for(int i=0;i<m;i++) for(int j=0;j<m;j++) if(a[i]<a[j]) tmp[i]=(tmp[i]+1ll*ans[i]*res[j])%mod;
else tmp[j]=(tmp[j]+1ll*ans[i]*res[j])%mod;
for(int i=0;i<m;i++) ans[i]=tmp[i];
break;
}
case '>':{
memset(tmp,0,sizeof tmp);
for(int i=0;i<m;i++) for(int j=0;j<m;j++) if(a[i]>a[j]) tmp[i]=(tmp[i]+1ll*ans[i]*res[j])%mod;
else tmp[j]=(tmp[j]+1ll*ans[i]*res[j])%mod;
for(int i=0;i<m;i++) ans[i]=tmp[i];
break;
}
case '?':{
memset(tmp,0,sizeof tmp);
for(int i=0;i<m;i++) for(int j=0;j<m;j++) tmp[i]=(tmp[i]+1ll*ans[i]*res[j])%mod,tmp[j]=(tmp[j]+1ll*ans[i]*res[j])%mod;
for(int i=0;i<m;i++) ans[i]=tmp[i];
break;
}
}
}
return ans;
}
inline void work(){
for(int i=0;i<m;i++) for(int j=1;j<=n;j++) a[j][i]=star::a[i][j-1];
for(int i=1;i<=n;i++){
for(int j=1;j<=len;j++) for(int k=0;k<m;k++) but[j][k]=(isdigit(s[j]) and s[j]-'0'==k);
int* res=solve(1,len,a[i]);
for(int j=0;j<m;j++)
ans=(ans+1ll*res[j]*a[i][j])%mod;
}
printf("%d\n",ans);
}
即把 \(n\) 组数分开来讨论,求每种数贡献的方案数,最后乘上值加起来。
那么问题就在于求每种数的方案数,这样的工作我们需要做 \(n\) 遍,非常不划算。
然后发现,对于大小关系相同的每组数,每种数贡献的方案数是相同的,改变的只是它们的值,我们没有必要每次都算一遍方案数。
发现,一个数在一组数中的相对大小关系的情况总数只有 \(2^m\) 种,即是否比该数大。
那么我们预处理出一个数的所有相对大小关系总数(这个部分是 \(O(2^m|E|)\) 的),然后对于 \(n\) 组数每个枚举 \(m\) 个数差分统计答案即可(差分得到该数的方案数。此部分是 \(O(nmlogm)\) 的)。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<utility>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=5e4+10,maxm=12,S=1050,mod=1e9+7;
int n,m,a[maxn][maxm],D,f[S],pos,b[maxm],ans;
char s[maxn];
pair<int,int> solve(){
int num=0;bool ok=true;char lst='>';
pair<int,int> ans(1,0),now,tmp;
while(lst!=')' and ++pos)
if(isdigit(s[pos])) num=s[pos]^48;
else if(s[pos]=='(') now=solve(),ok=false;
else ok and (now=make_pair((D>>num)&1,(D>>num)&1^1),0),
ok=true,tmp=make_pair(0,0),
lst!='>' and (tmp.second=(tmp.second+1ll*ans.second*now.second)%mod,tmp.first=(tmp.first+1ll*(ans.first+ans.second)*(now.first+now.second)-1ll*ans.second*now.second%mod+mod)%mod),
lst!='<' and (tmp.first=(tmp.first+1ll*ans.first*now.first)%mod, tmp.second=(tmp.second+1ll*(ans.first+ans.second)*(now.first+now.second)-1ll*ans.first*now.first%mod+mod)%mod),
ans=tmp,lst=s[pos],num=0;
return ans;
}
inline bool cmp(const int &x,const int &y){return a[pos][x]<a[pos][y];}
inline void work(){
n=read(),m=read();
for(int i=0;i<m;i++) for(int j=0;j<n;j++) a[j][i]=read();
scanf("%s",s+1);s[strlen(s+1)+1]=')';
for(D=0;D<(1<<m);D++) pos=0,f[D]=solve().second;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++) b[j]=j;
pos=i;sort(b,b+m,cmp);
for(int j=0,d=0;j<m;d|=(1<<b[j++]))
ans=(ans+1ll*(a[i][b[j]]-(j?a[i][b[j-1]]:0))*f[d])%mod;
}
printf("%d\n",ans);
}
}
signed main(){
star::work();
return 0;
}
P7324 [WC2021] 表达式求值的更多相关文章
- 洛谷 P7324 - [WC2021] 表达式求值(状压+dp)
题面传送门 现场人傻系列-- 首先建出 \(E\) 的表达式树,具体来说表达式的每一个叶子节点表示一个数组 \(A_i\),每一个非叶子节点都表示一次运算,它的值表示左右儿子进行该运算后得到的结果.这 ...
- [WC2021] 表达式求值
考虑我们显然可以对每位分开求解,考虑求出最终答案是\(A_i\)的方案数. 那么我们发现我们这样\(dp\)的话,显然不太行! 会有一个\(i\)的复杂度 但是如果我们做大于等于的话,就只用一遍\(d ...
- 表达式求值(noip2015等价表达式)
题目大意 给一个含字母a的表达式,求n个选项中表达式跟一开始那个等价的有哪些 做法 模拟一个多项式显然难以实现那么我们高兴的找一些素数代入表达式,再随便找一个素数做模表达式求值优先级表 - ( ) + ...
- 用Python3实现表达式求值
一.题目描述 请用 python3 编写一个计算器的控制台程序,支持加减乘除.乘方.括号.小数点,运算符优先级为括号>乘方>乘除>加减,同级别运算按照从左向右的顺序计算. 二.输入描 ...
- 数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值
一.简介 迷宫求解:类似图的DFS.具体的算法思路可以参考书上的50.51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意.大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚, ...
- nyoj305_表达式求值
表达式求值 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近又学会了一些简单的函数求值,比如,它知道函数min ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- 数据结构--栈的应用(表达式求值 nyoj 35)
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=35 题目: 表达式求值 时间限制:3000 ms | 内存限制:65535 KB描述 AC ...
- NOIP2013普及组 T2 表达式求值
OJ地址:洛谷P1981 CODEVS 3292 正常写法是用栈 #include<iostream> #include<algorithm> #include<cmat ...
随机推荐
- Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析
NLP (Natural Language Processing) 是人工智能(AI)的一个子领域.自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一(来自百度百科). 其中中文更是 ...
- Kubernetes 实战——升级应用(Deployment)
一.更新运行在 Pod 内的应用程序 1. 修改 Pod 模板 将导致应用程序在一定时间内不可用 2. 修改 Service 的 Pod 选择器 需要同时运行两倍的 Pod 3. 滚动升级 应用程序需 ...
- 并发王者课-铂金2:豁然开朗-“晦涩难懂”的ReadWriteLock竟如此妙不可言
欢迎来到<并发王者课>,本文是该系列文章中的第15篇. 在上篇文章中,我们介绍了Java中锁的基础Lock接口.在本文中,我们将介绍Java中锁的另外一个重要的基本型接口,即ReadWri ...
- Linux命令基础(二)
一.列表显示目录内容-ls 1.显示目录中内容,包括子目录和文件相关属性信息 ls(列表的形式去显示目录内容) [选项](可有可无的) ...
- java中的关键字volatile
1.volatile简介 volatile作为java中的关键词之一,用以声明变量的值可能随时会被别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值 ...
- WEB安全漏洞扫描与处理(下)——安全报告分析和漏洞处理
1 AppScan生成的安全报告分析 利用AppScan生成安全报告,可以提前对要生成的安全报告的内容进行选择,如下图,最全的安全报告内容,包括摘要,安全性问题,咨询和修订建议,应用程序数据等. 生成 ...
- Golang写文件的坑
Golang写文件一般使用os.OpenFile返回文件指针的Write方法或者WriteString或者WriteAt方法,但是在使用这三个方法时候经常会遇到写入的内容和实际内容有出入,因为这几个函 ...
- Vue(12)组件的组织结构和组件注册
组件的组织 通常一个应用会以一棵嵌套的组件树的形式来组织: 例如,你可能会有页头.侧边栏.内容区等组件,每个组件又包含了其它的像导航链接.博文之类的组件. 为了能在模板中使用,这些组件必须先注册以便 ...
- QT 之 ODBC连接人大金仓数据库
QT 之 使用 ODBC 驱动连接人大金仓数据库 获取数据库驱动和依赖动态库 此操作可在人大金仓官网下载与系统匹配的接口动态库,或者从架构数据库的源码中获取驱动和依赖动态库 分别为: 驱动动态库:kd ...
- 关于asp.net中Repeater控件的一些应用
在Asp.net中,我是比较喜欢用Repeater这个控件,刚刚遇到的一个问题,怎么实现单击 <asp:LinkButton>,通过后台的单击事件获取同一行数据中的其他数据(对象). 1, ...