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为根子 ...
随机推荐
- vue安装
条件:已安装 node&npm 1.安装 cnpm : $ npm install -g cnpm --registry=https://regis ...
- hibernate 入门([数据访问中间件] 开源框架)
1.内容: hibernate 也是一个经典的[数据访问中间件] 开源框架. 2.hibernate核心组件 SessionFactory[整个数据的操作]重量级组件 ...
- arcgis engine 调用arcgis server服务
首先需要添加两个引用: using ESRI.ArcGIS.GISClient;using ESRI.ArcGIS.DataSourcesRaster; /// <summary> /// ...
- SharePoint 2013 数据库中手动更新用户信息
在SharePoint的使用过程中,尤其是Windows认证的情况下,而且没有配置用户配置文件服务,经常会出现如果更新AD中的用户信息(包括名字.显示名.邮件等),SharePoint这边站点并不会更 ...
- 使用MDK3探测隐藏SSID名称
先将网卡设置为监听模式. airmon-ng start wlan0 扫描附近无线网络,找出隐藏SSID的AP. airodump-ng wlan0mon 这里用下图搜到的第一个隐藏SSID做测试. ...
- Android 进程生命周期 Process Lifecycle
Android 进程生命周期 Process Lifecycle 进程的生命周期 Android系统会尽力保持应用的进程,但是有时为了给新的进程和更重要的进程回收一些内存空间,它会移除一些旧的进程. ...
- 检查sqlite数据库完整性
最近遇到一个问题,用户数据丢失,拿到用户数据库文件以后,发现数据库损坏. database disk image is malformed 因此希望可以找到一种方法,可以检测出来数据库是否损坏,经过g ...
- 复杂sql分组查询 ( pivot)
一个数据表里面字段有年.月.日.金额.支付方式等字段,然后现在想写个sql语句,把每一天的每种支付方式金额(支付方式有多重)排在同一行, 最后在增加一列小计当前的所有支付方式的金额.如下图: 原sql ...
- Android 4.4沉浸式状态栏的实现
要实现Android 4.4上面的沉浸式状态栏要用到开源项目SystemBarTint(https://github.com/hexiaochun/SystemBarTint) public clas ...
- Android 手机卫士--实现设置界面的一个条目布局结构
本文地址:http://www.cnblogs.com/wuyudong/p/5908986.html,转载请注明源地址. 本文以及后续文章,将一步步完善功能列表: 要点击九宫格中的条目,需要注册点击 ...