大眼一看最下面的题意解释的话,发现这和洛谷P1310表达式的值挺像的,大概都是给定一些运算符号,让最后的表达式为true的概率,为false的概率啥的QwQ~;

然后这个题嘛?就是在所有的运算符中提溜出一个作为最后一次运算的运算符,然后我们去算这个运算符左边的那一堆式子,然后再算右边那一堆式子,最后再算一下最后一个运算符对应的运算就好啦!

那么怎么算这个运算符左边的式子和右边的式子呢?我们再从左边的式子中找出一个运算符最为最后一次运算的运算符,然后算它左边的式子,算它右边的式子……

我们发现这就是一个枚举 + 递归的过程是吧QwQ~

直到我们一直递归到式子长度为1的情况了,这时候如果是' t '那就对应一种表达式为true的情况,如果是' f '那就对应了一种表达式为false的情况;

我们开两个数组 t [ i ][ j ]表示区间 [ i,j ]的表达式为true的方案数,f [ i ][ j ]表示区间 [ i,j ]的表达式为false的方案数,那么有如下边界设置:

    for(int i=;i<=n;i++)
{
if(s[i]=='t') //贡献了一个为true的方案
{
t[i][i]=,f[i][i]=;
}
else //否则就贡献一个为false的方案
{
f[i][i]=,t[i][i]=;
}
}
//s数组里存的是't' or 'f'

那么最终的答案就是:

然后 t 和 f 都是遍历区间内所有的运算符,假设作为最后一个运算,根据运算符的规则,递归求左子区间和右子区间的答案,相乘。最后把所有可能的情况累加起来,返回。
就是一个递归的思路,这里用了记忆化的原理,开数组将每种情况的方案数存起来。
但是时间复杂度还是比较高,对于这个题还是会炸!
那么这个题的正解就是:区间DP!
参考我们当时做洛谷P1040 加分二叉树的思路(不知道的先看一下这里):
我们也是推出了对于每个数字,让它作为根结点,然后算左子树,算右子树,最后再算整体的;这个思路不是和本题很相似嘛?
所以我们仍然可以套上之前我们区间DP的做法:
按照区间长度从小到大开始,枚举每个区间,算出每个区间内表达式为true的方案数,为false的方案数;
利用我们之前求出的小区间,然后一步一步得推出更大区间的答案,最后覆盖整个大区间的答案;
 
先献上区间DP的一般套路:
    for(int len=;len<=n;len++)          //枚举区间长度
for(int i=;i+len-<=n;i++) //枚举区间左端点的位置
{
int j=i+len-; //计算出区间右端点的位置
for(int k=i;k<j;k++) //枚举区间内的中间点
……………
}

已经确定了要使用区间DP了,接下来的任务就是要找状态转移方程了(下面的过程就和分析P1310表达式的值 的时候几乎一样了);

题目中只有三种运算符:

与&,或 |,异或 ^,先了解一下它们的运算规则:

&:要使 x&y=1,x和y必须都为1;要使 x&y=0,x和y有一个为0;

| :要使x | y=1,x和y有一个为1;要使x | y=0,x和y必须都为0;

^:要使x ^ y=1,x和y必须不相同(即x=0,y=1或 x=1,y=0);要使x ^ y=0,x和y必须相同(即x=0,y=0或 x=1,y=1);

然后我们就可以根据它们的运算法则来确定状态转移方程啦(还运用了乘法计数原理):

    for(int len=;len<=n;len++)          //枚举区间长度
{
for(int i=;i+len-<=n;i++) //枚举区间左端点的位置
{
int j=i+len-; //计算出区间右端点的位置
for(int k=i;k<j;k++) //枚举区间内的中间点的运算符号
{
if(ops[k]=='&') //&运算
{
t[i][j]=(t[i][j]+(t[i][k]*t[k+][j])%mod)%mod; //必须两个都为true
f[i][j]=(f[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod+(f[i][k]*f[k+][j])%mod)%mod; //只需要其中一个为false
}
if(ops[k]=='|') //|运算
{
t[i][j]=(t[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod+(t[i][k]*t[k+][j])%mod)%mod; //只需要其中一个为true
f[i][j]=(f[i][j]+(f[i][k]*f[k+][j])%mod)%mod; //必须两个都为false
}
if(ops[k]=='^') //异或运算
{
t[i][j]=(t[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod)%mod; //必须两个不同
f[i][j]=(f[i][j]+(f[i][k]*f[k+][j])%mod+(t[i][k]*t[k+][j])%mod)%mod; //必须两个相同
}
}
}
}

注意到我们的答案是需要取模的,而最后的答案还涉及到了除法,所以最后我们还需要一步求乘法逆元的过程!

这里我用的是扩展欧几里得求逆元,这个大家应该都会吧QwQ~
上代码喽!
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int X=,w=;
char c=getchar();
while(c<''||c>'')
{
if (c=='-')
{
w=-;
}
c=getchar();
}
while(c>=''&&c<='')
{
X=(X<<)+(X<<)+c-'';
c=getchar();
}
return X*w;
}
inline char gc()
{
char c;
do
{
c=getchar();
}
while(c==' '||c=='\n'||c=='\r'||c=='\0'||c=='\t');
}
int n;
long long x,y;
char s[],ops[];
long long t[][],f[][];
const int mod=;
void exgcd(long long a,long long b,long long &x,long long &y) //扩展欧几里得求逆元
{
if(b==)
{
x=;y=;return ;
}
exgcd(b,a%b,x,y);
long long r=x;
x=y;
y=r-(a/b)*x;
}
int main()
{
n=read();
for(int i=;i<=n-;i++)
{
s[i]=gc(); //存't' or 'f'
ops[i]=gc(); //存运算符
}
//这么读入的原因是注意到't','f'是和运算符一个一个隔开的
s[n]=gc();
for(int i=;i<=n;i++)
{
if(s[i]=='t') //边界条件
{
t[i][i]=,f[i][i]=;
}
else
{
f[i][i]=,t[i][i]=;
}
}
for(int len=;len<=n;len++) //枚举区间长度
{
for(int i=;i+len-<=n;i++) //枚举区间左端点的位置
{
int j=i+len-; //计算出区间右端点的位置
for(int k=i;k<j;k++) //枚举区间内的中间点
{
if(ops[k]=='&') //&运算
{
t[i][j]=(t[i][j]+(t[i][k]*t[k+][j])%mod)%mod; //必须两个都为true
f[i][j]=(f[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod+(f[i][k]*f[k+][j])%mod)%mod; //只需要其中一个为false
}
if(ops[k]=='|') //|运算
{
t[i][j]=(t[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod+(t[i][k]*t[k+][j])%mod)%mod; //只需要其中一个为true
f[i][j]=(f[i][j]+(f[i][k]*f[k+][j])%mod)%mod; //必须两个都为false
}
if(ops[k]=='^') //异或运算
{
t[i][j]=(t[i][j]+(t[i][k]*f[k+][j])%mod+(f[i][k]*t[k+][j])%mod)%mod; //必须两个不同
f[i][j]=(f[i][j]+(f[i][k]*f[k+][j])%mod+(t[i][k]*t[k+][j])%mod)%mod; //必须两个相同
}
}
}
}
exgcd(t[][n]+f[][n],mod,x,y);
cout<<t[][n]*((x+mod)%mod)%mod;
return ;
}

2019.6.28 校内测试 T3 【音乐会】道路千万条的更多相关文章

  1. 2019.6.28 校内测试 T4 【音乐会】达拉崩吧·上

    考试的一道附加题~ 一看题目描述:把区间[l,r]里每个数异或上x,求区间[l,r]里所有数的异或和,这明显的要用数据结构或RMQ吧. 恩,所以正解就是线段树啦,至于树状数组行与否,不知道~ wate ...

  2. 2019.6.28 校内测试 T2 【音乐会】二重变革

    看到这个题之后,一个很暴力很直接的想法就是贴上题目中的代码然后交上去走人,但是很显然这是会TLE+MLE的,想想谁会这么傻把主要代码给你QwQ~: 其实这段代码是想告诉你一件事:用序列中的大数减去小数 ...

  3. 2019.7.9 校内测试 T3 15数码问题

    这一次是交流测试?边交流边测试(滑稽 15数码问题 大家应该都玩过这个15数码的游戏吧,就在桌面小具库那里面哦. 一看到这个题就知道要GG,本着能骗点分的原则输出了 t 个无解,本来以为要爆零,没想到 ...

  4. 2019.6.28 校内测试 T1 Jelly的难题1

    这题面有点难理解,建议直接跳到题意解释那一部分(虽然我觉得解释的不大对,但按照解释来做确实能AC): 按照“题意解释”的思路来思考这个题,那么就十分的简单了: 1.首先要读入这个字符矩阵,可以用cin ...

  5. 【6.28校内test】T3 【音乐会】道路千万条

    [音乐会]道路千万条[题目链接] 首先,你可以忽略上面的一大坨题面,只需要看说明的那一小部分就好啦. 然后理解题意: 就是说我们要给这n-1个运算符指定一个顺序,统计所有值为true的方案数pt,统计 ...

  6. 2019.7.9 校内测试 T2 极值问题

    这一次是交流测试?边交流边测试(滑稽 极值问题 乍一看这是一道数学题,因为1e9的数据让我暴力的心退却. 数学又不好,不会化简式子嘞,咋办? 不怕,咱会打表找规律.(考场上真的是打表找出了规律,打表打 ...

  7. 2019.7.9 校内测试 T1挖地雷

    这一次是交流测试?边交流边测试(滑稽 挖地雷 这个题是一个递推问题. 首先我们看第一个格子,因为它只影响了它的上面和右上面这两个地方是否有雷. 我们可以分3种情况讨论: 1. 第一个格子的数字是2: ...

  8. 2019.6.20 校内测试 NOIP模拟 Day 1 分析+题解

    这次是zay神仙给我们出的NOIP模拟题,不得不说好难啊QwQ,又倒数了~ T1 大美江湖 这个题是一个简单的模拟题.   ----zay 唯一的坑点就是打怪的时候计算向上取整时,如果用ceil函数一 ...

  9. 【8.28校内测试】【区间DP】

    感受到了生活的艰辛QAQ...这才是真正的爆锤啊...(因为t1t3还没有理解所以只能贴t2叻QAQ 区间DP...爆哭把题理解错了,以为随着拿的东西越来越多,断点也会越来越多,出现可以选很多的情况Q ...

随机推荐

  1. array_chunk — 将一个数组分割成多个

    说明 array_chunk ( array $array , int $size [, bool $preserve_keys = false ] ) : array 将一个数组分割成多个数组,其中 ...

  2. restTemplate源码解析(一)构造restTemplate的Bean实例

    所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 构造一个restTemplate的Bean实例很容易,只需这样配置 @Bean publ ...

  3. swiper按钮点击无效及控制器无效问题

    点击箭头图片切换的同时,下面小图标也会随着切换,同理下面小图标切换时,上面也随着滚动. 示例代码如下: <!-- Swiper --> <div class="swiper ...

  4. 【转载】salesforce 零基础开发入门学习(一)Salesforce功能介绍,IDE配置以及资源下载

    salesforce 零基础开发入门学习(一)Salesforce功能介绍,IDE配置以及资源下载   目前国内已经有很多公司做salesforce,但是国内相关的资料确是少之又少.上个月末跳槽去了新 ...

  5. mysql使用GTID跳过事务

    GTID跳过有两种方法,一种是普通的跳过一个事务的方法,另外一个是在基于主库搭建新的slave的时候.一.普通跳过一个事务的方法.通过show slave status\G找到冲突的GTID号.然后执 ...

  6. 【Mysql MHA】CentOS7.6+Mysql8.0.16 入坑

    1.防火墙 firewall-cmd --add-port=/tcp --permanent firewall-cmd --reload 2.SELINUX sed -i 's/SELINUX=enf ...

  7. WIN10安装.net报0x800F081F解决方法

    WIN10安装.net2.0和.net3.0报错,错误代码:0x800F081F,解决方法: 方法一:检查服务windows update有无开启,若未开启,开启服务后,再装.net 注:若安装.ne ...

  8. 03 WIndows编程——手绘函数调用过程

    源码 #include<Windows.h> #include<stdio.h> int MessageBoxPrint(char *szFormat, ...); int W ...

  9. c线程使用锁控制并发

    // // Created by gxf on 2019/12/16. // #include <stdlib.h> #include <stdio.h> #include & ...

  10. XPath 爬虫解析库

    XPath     XPath,全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言.最初是用来搜寻 XML 文档的,但同样适用于 HTML 文档的 ...