NOIP2011pj表达式的值[树形DP 笛卡尔树 | 栈 表达式解析]
题目描述
对于1 位二进制变量定义两种运算:

运算的优先级是:
先计算括号内的,再计算括号外的。
- “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算。例如:计算表达式A⊕B × C时,先计算 B × C,其结果再与 A 做⊕运算。
现给定一个未完成的表达式,例如+(*_),请你在横线处填入数字0 或者1 ,请问有多少种填法可以使得表达式的值为0 。
输入输出格式
输入格式:
输入文件名为exp.in ,共 2 行。
第1 行为一个整数 L,表示给定的表达式中除去横线外的运算符和括号的个数。
第2 行为一个字符串包含 L 个字符,其中只包含’(’、’)’、’+’、’*’这4 种字符,其中’(’、’)’是左右括号,’+’、’*’分别表示前面定义的运算符“⊕”和“×”。这行字符按顺序给出了给定表达式中除去变量外的运算符和括号
输出格式:
输出文件exp.out 共1 行。包含一个整数,即所有的方案数。注意:这个数可能会很大,请输出方案数对10007 取模后的结果。
输入输出样例
4
+(*)
3
说明
【输入输出样例说明】
给定的表达式包括横线字符之后为:+(*_)
在横线位置填入(0 、0 、0) 、(0 、1 、0) 、(0 、0 、1) 时,表达式的值均为0 ,所以共有3种填法。
【数据范围】
对于20% 的数据有 0 ≤ L ≤ 10。
对于50% 的数据有 0 ≤ L ≤ 1,000。
对于70% 的数据有 0 ≤ L ≤ 10,000 。
对于100%的数据有 0 ≤ L ≤ 100,000。
对于50% 的数据输入表达式中不含括号。
-------------------------
一开始想了一个区间DP的做法f[i][j][0/1]表示i到j为0或1的方案数,内存爆的连编译都不编译
然后想到建表达式树 树形DP,白书上的方法只能拿80分
用 笛卡尔树 建表达式树
有点像treap,key按左右分,value按上下分
表达式树中key就是顺序,本来就按照这个顺序;value是算术优先级,设括号个数为p,'+':p*2+1 '*':p*2+2,value小的在上面
笛卡尔树有O(n)的建树方法:
可以发现key本来有序,新加的元素只能在当前的右链上,有可能吧本来右链上一些元素转到左子树上
用一个stack维护右链上的元素就好了,每次找第一个<=当前的
//
// main.cpp
// 表达式的值树形dp
//
// Created by Candy on 9/6/16.
// Copyright ? 2016 Candy. All rights reserved.
// #include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N=,MOD=;
int n;
char s[N];
struct node{
int ls,rs;
char op;
}tree[N*];
int cnt=,w[N],root=;
void build(){
int p=,cnt=;
for(int i=;i<=n;i++){
if(s[i]=='(') p++;if(s[i]==')') p--;
if(s[i]=='+') {w[++cnt]=p*+;
tree[cnt].op=s[i];
}
if(s[i]=='*') w[++cnt]=p*+,tree[cnt].op=s[i];;
} int st[N],k,top=-;
for (int i=;i<=cnt;i++)
{
k = top;
while (k >= && w[st[k]] > w[i]) k--;
if(k!=-) tree[st[k]].rs=i;
if (k < top) tree[i].ls=st[k+];
st[++k] = i;
top=k;
}
root=st[];
}
int f[N][];
void dp(int i){//printf("dp %d\n",i);
if(i==) return;
if(f[i][]!=) return;
int ls=tree[i].ls,rs=tree[i].rs;char op=tree[i].op;
dp(ls);dp(rs);
if(op=='+'){
f[i][]=f[ls][]*f[rs][];
f[i][]=f[ls][]*f[rs][]+f[ls][]*f[rs][]+f[ls][]*f[rs][];
}
if(op=='*'){
f[i][]=f[ls][]*f[rs][];
f[i][]=f[ls][]*f[rs][]+f[ls][]*f[rs][]+f[ls][]*f[rs][];
}
f[i][]%=MOD;f[i][]%=MOD;
}
int main(int argc, const char * argv[]) {
scanf("%d%s",&n,s+);
build();
f[][]=f[][]=;
dp(root);
printf("%d",f[root][]%MOD);
return ;
}
当然也可以用stack做,一个操作符栈一个数据栈,数据栈中是0/1的方案数
一开始push一个empty,以后每次一个+ *都push一个empty
//from 题解
//Candy?修改
#include<cstdio>
#include<cstring>
const int mod=;
struct node{
int a,b;
}f[];
const node emp=(node){,};
char s[];
char st[];int n,tp=,fp=; void cal(char op,node &a,node &b){
if(op=='+') a.b=(a.b*(b.a+b.b)+a.a*b.b)%mod,a.a=a.a*b.a%mod;
else a.a=(a.a*(b.a+b.b)+a.b*b.a)%mod,a.b=a.b*b.b%mod;
} int main(){
scanf("%d%s",&n,s);
st[++tp]='('; f[++fp]=emp;
s[n++]=')';
for(int i=;i<n;i++){
if(s[i]=='(')st[++tp]='(';
else if(s[i]==')'){
for(;st[tp]!='(';tp--,fp--)
cal(st[tp],f[fp-],f[fp]);//pop 2 push 1
tp--;// (
}
else{
for(;st[tp]<=s[i]&&st[tp]!='(';tp--,fp--)// '*' < '+'
cal(st[tp],f[fp-],f[fp]);
st[++tp]=s[i],f[++fp]=emp;//every +/* with a number
}
}
return!printf("%d\n",f[].a%mod);
}
NOIP2011pj表达式的值[树形DP 笛卡尔树 | 栈 表达式解析]的更多相关文章
- HDU 1506 Largest Rectangle in a Histogram(单调栈、笛卡尔树)
题意:给定n个连续排列的矩形的高,矩形的宽都为1.问最大矩形覆盖. 例如:n = 7,h[i] = (2 1 4 5 1 3 3),最大覆盖为8. Sample Input 7 2 1 4 5 1 3 ...
- BZOJ2616 SPOJ PERIODNI(笛卡尔树+树形dp)
考虑建一棵小根堆笛卡尔树,即每次在当前区间中找到最小值,以最小值为界分割区间,由当前最小值所在位置向两边区间最小值所在位置连边,递归建树.那么该笛卡尔树中的一棵子树对应序列的一个连续区间,且根的权值是 ...
- 【BZOJ2616】SPOJ PERIODNI 笛卡尔树+树形DP
[BZOJ2616]SPOJ PERIODNI Description Input 第1行包括两个正整数N,K,表示了棋盘的列数和放的车数. 第2行包含N个正整数,表示了棋盘每列的高度. Output ...
- BZOJ.2616.SPOJ PERIODNI(笛卡尔树 树形DP)
BZOJ SPOJ 直观的想法是构建笛卡尔树(每次取最小值位置划分到两边),在树上DP,这样两个儿子的子树是互不影响的. 令\(f[i][j]\)表示第\(i\)个节点,放了\(j\)个车的方案数. ...
- bzoj 2616 SPOJ PERIODNI——笛卡尔树+树形DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2616 把相同高度的连续一段合成一个位置(可能不需要?),用前缀和维护宽度. 然后每次找区间里 ...
- 洛谷 P5044 - [IOI2018] meetings 会议(笛卡尔树+DP+线段树)
洛谷题面传送门 一道笛卡尔树的 hot tea. 首先我们考虑一个非常 naive 的区间 DP:\(dp_{l,r}\) 表示区间 \([l,r]\) 的答案,那么我们考虑求出 \([l,r]\) ...
- codevs2178 表达式运算Cuties[笛卡尔树]
2178 表达式运算Cuties 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 大师 Master 题解 查看运行结果 题目描述 Description 给出一个表达 ...
- POJ 3162 bit区间查询最值+树形DP
POJ 3162 『题目链接』POJ 3162 『题目类型』bit区间查询最值+树形DP ✡Problem: 一棵n个节点的树.wc爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远 ...
- bzoj2616: SPOJ PERIODNI——笛卡尔树+DP
不连续的处理很麻烦 导致序列DP又找不到优秀的子问题 自底向上考虑? 建立小根堆笛卡尔树 每个点的意义是:高度是(自己-father)的横着的极大矩形 子问题具有递归的优秀性质 f[i][j]i为根子 ...
随机推荐
- JavaScript学习笔记-自定义集合类
//集合类Set( ES6标准才有的类,目前兼容性较差)//自定义集合类:extend = function (o,p){ //定义一个复制对象属性的类函数 for(var x in p){ o[x] ...
- JavaScript 左右上下自动晃动,自动移动。
最近做了一个项目,本来用css3动画做的,不兼容ie,用js做了个,分享给大家. 代码修改了下,上下左右四个模块,顺时针转动. <!DOCTYPE html> <html> & ...
- Ajax基本概念和原理
什么是Ajax Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术. Ajax的全称是Asynchronous JavaScript and XML,即异步JavaScript+X ...
- console命令详解
Firebug是网页开发的利器,能够极大地提升工作效率. 但是,它不太容易上手.我曾经翻译过一篇<Firebug入门指南>,介绍了一些基本用法.今天,继续介绍它的高级用法. ======= ...
- MSCRM 2011/2013/2015 修改显示记录数
本文地址:http://www.cnblogs.com/Earson/p/4256213.html 1.针对全局的显示记录数最大值设置 在CRM2011产品中的后台MSCRM_Config数据库中表名 ...
- Fragment中的按键监听
在Fragmentzhong中写按键监听,有两处处需要注意: 1)是否是当前显示的fragment:. 2)在所依托的activity中的onKeyDown方法处理监听事件: 其他地方和普通按键监听一 ...
- 巩固一下:SpringMVC详细示例实战教程
一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 1 2 3 4 5 6 ...
- IOS客户端Coding项目记录(六)
1:获取某一行的坐标 UITableViewCell *cell = [_myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow: ...
- sublime text 3 常用快捷键 、常用插件
常用快捷键 查找( Ctrl + P ) 找到任何东西 - :+行号 定位到具体的行 - @+符号 js的函数名, css的选择器名 - #+关键字 定位到特定的关键字 命令面板 (Ctrl ...
- lumen Console Commands
1.在app->Console->Commands中新增类 继承 Illuminate\Console\Command <?php namespace App\Console\Co ...