题目传送门(内部题16)


输入格式

第一行两个整数$n$和$m$,代表网格的大小。
接下来$n$行每行一个长度为$m$的字符串,每个字符若为$W$代表这个格子必须为阳,若为$B$代表必须为阴,若为$?$代表可以运功调整。


输出格式

一行一个整数,代表阴阳平衡的方案数模$1e9+7$的余数。


样例

样例输入1:

3 3
B?W
?B?
???

样例输出1:

8

样例输入2:

3 3
???
???
???

样例输出2:

66


数据范围与提示

对于$30\%$的数据,$n\leqslant 4,m\leqslant 4$。
对于另外$30\%$的数据,所有格子均可调整
对于$80\%$的数据,$n\leqslant 100,m\leqslant 100$。
对于$100\%$的数据,$n\leqslant 1,000,m\leqslant 1,000$。


题解

乍一看这道题似乎不可做,那么考虑如何转化问题,不妨先画两张图?

然后我们发现,合法情况要满足如下两条性质:

  $\alpha.$黑色和白色的格子各自形成了两个联通块。

  $\beta.$白色格子与黑色格子的分界处自上而下形成了单调不降或单调不增的序列。

那么我们考虑如何统计答案,不妨先考虑黑色位于左侧,分界线成单调不降的方案数。

首先,设$can[i][j]$表示第$i$行第$1$列到第$j$列均可被染成黑色并且第$j+1$列到第$m$列均可被染成白色,用$0/1$表示。

那么,状态转移方程即为:$dp[i][j]=[can[i][j]]\sum \limits_{k=1}{j}dp[i-1][k]$。

但是显然这个转移是$\Theta(n^3)$的,死不死撒?前缀和优化不就好了嘛~

同理,还有以下三种情况:

  $\alpha.$黑色居于左侧,分界点单调不增。

  $\beta.$白色居于左侧,分界点单调不降。

  $\gamma.$白色居于左侧,分界点单调不增。

然后你就把答案相加了,哭哭涕涕的问我为什么不对,其实仔细想一想,会有重复的情况,比方说下面这几种:

  $\alpha.$每行颜色相同,黑色位于上方:

  

  反之同理。

  $\beta.$每列颜色相同,黑色位于左侧:

  

  反之同理。

  $\gamma.$全白或全黑:

  

这样,你就可以开心的$AC$此题了。

时间复杂度:$\Theta(n^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m,op=0;
char ch[1001];
int can[1001][2][2];
long long dp[1001][1001][2][2],s[1001][2][2];
long long ans;
int main()
{
scanf("%d%d",&n,&m);
can[0][0][0]=0;
can[0][0][1]=m;
can[0][1][0]=1;
can[0][1][1]=m+1;
for(int i=1;i<=n;i++)
{
can[i][0][0]=0;
can[i][0][1]=m;
can[i][1][0]=1;
can[i][1][1]=m+1;
scanf("%s",ch+1);
for(int j=1;j<=m;j++)
{
if(ch[j]=='W')
{
can[i][0][0]=max(can[i][0][0],j);
can[i][1][1]=min(can[i][1][1],j);
if(op==0)op=1;
if(op==2)op=3;
}
if(ch[j]=='B')
{
can[i][0][1]=min(can[i][0][1],j-1);
can[i][1][0]=max(can[i][1][0],j+1);
if(op==0)op=2;
if(op==1)op=3;
}
}
}
switch(op)
{
case 0:ans=2;break;
case 1:ans=1;break;
case 2:ans=1;break;
}
dp[0][0][0][0]=dp[0][m][0][1]=1;
dp[0][m+1][1][0]=dp[0][1][1][1]=1;
for(int i=1;i<=n;i++)
{
s[0][0][0]=dp[i-1][0][0][0];s[0][0][1]=dp[i-1][0][0][1];
s[m+1][1][0]=dp[i-1][m+1][1][0];s[m+1][1][1]=dp[i-1][m+1][1][1];
for(int j=1;j<=m;j++)
{
s[j][0][0]=(s[j-1][0][0]+dp[i-1][j][0][0])%1000000007;
s[j][0][1]=(s[j-1][0][1]+dp[i-1][j][0][1])%1000000007;
}
for(int j=m;j>=1;j--)
{
s[j][1][0]=(s[j+1][1][0]+dp[i-1][j][1][0])%1000000007;
s[j][1][1]=(s[j+1][1][1]+dp[i-1][j][1][1])%1000000007;
}
for(int j=0;j<=m;j++)
{
if(j>=can[i][0][0]&&j<=can[i][0][1])
{
if(j>=can[i-1][0][0])
{
dp[i][j][0][0]=s[min(j,can[i-1][0][1])][0][0];
if(can[i-1][0][0]>0)dp[i][j][0][0]-=s[can[i-1][0][0]-1][0][0];
dp[i][j][0][0]=(dp[i][j][0][0]%1000000007+1000000007)%1000000007;
}
if(j<=can[i-1][0][1])
{
dp[i][j][0][1]=s[can[i-1][0][1]][0][1];
if(max(can[i-1][0][0],j)>0)dp[i][j][0][1]-=s[max(can[i-1][0][0],j)-1][0][1];
dp[i][j][0][1]=(dp[i][j][0][1]%1000000007+1000000007)%1000000007;
}
}
}
for(int j=m+1;j;j--)
{
if(j>=can[i][1][0]&&j<=can[i][1][1])
{
if(j<=can[i-1][1][1])
{
dp[i][j][1][0]=s[max(j,can[i-1][1][0])][1][0];
if(can[i-1][1][1]<=m) dp[i][j][1][0]-=s[can[i-1][1][1]+1][1][0];
dp[i][j][1][0]=(dp[i][j][1][0]%1000000007+1000000007)%1000000007;
}
if(j>=can[i-1][1][0]){
dp[i][j][1][1]=s[can[i-1][1][0]][1][1];
if(min(can[i-1][1][1],j)<=m) dp[i][j][1][1]-=s[min(can[i-1][1][1],j)+1][1][1];
dp[i][j][1][1]=(dp[i][j][1][1]%1000000007+1000000007)%1000000007;
}
}
}
}
for(int i=can[n][0][0];i<=can[n][0][1];i++)
{
ans=(ans+dp[n][i][0][0])%1000000007;
ans=(ans+dp[n][i][0][1])%1000000007;
}
for(int i=can[n][1][0];i<=can[n][1][1];i++)
{
ans=(ans+dp[n][i][1][0])%1000000007;
ans=(ans+dp[n][i][1][1])%1000000007;
}
int l1=0,r1=m,l2=1,r2=m+1;
for(int i=1;i<=n;i++)
{
l1=max(l1,can[i][0][0]);
r1=min(r1,can[i][0][1]);
l2=max(l2,can[i][1][0]);
r2=min(r2,can[i][1][1]);
}
if(l1<=r1)ans-=(r1-l1+1);
if(l2<=r2)ans-=(r2-l2+1);
int flag;
for(int i=0;i<=n;i++)
{
flag=1;
for(int j=1;j<=i;j++)
if(can[j][0][1]<m)
{flag=0;break;}
for(int j=i+1;j<=n;j++)
if(can[j][0][0]>0)
{flag=0;break;}
ans-=flag;
flag=1;
for(int j=1;j<=i;j++)
if(can[j][0][0]>0)
{flag=0;break;}
for(int j=i+1;j<=n;j++)
if(can[j][0][1]<m)
{flag=0;break;}
ans-=flag;
}
for(int i=0;i<=n;i++)
{
flag=1;
for(int j=1;j<=i;j++)
if(can[j][1][1]>1)
{flag=0;break;}
for(int j=i+1;j<=n;j++)
if(can[j][1][0]<=m)
{flag=0;break;}
ans-=flag;flag=1;
for(int j=1;j<=i;j++)
if(can[j][1][0]<=m)
{flag=0;break;}
for(int j=i+1;j<=n;j++)
if(can[j][1][1]>1)
{flag=0;break;}
ans-=flag;
}
printf("%lld",(ans%1000000007+1000000007)%1000000007);
return 0;
}

rp++

[CSP-S模拟测试]:阴阳(容斥+计数+递推)的更多相关文章

  1. 【BZOJ 4455】 [Zjoi2016]小星星 容斥计数

    dalao教导我们,看到计数想容斥……卡常策略:枚举顺序.除去无效状态.(树结构) #include <cstdio> #include <cstring> #include ...

  2. 数学(容斥计数):LNOI 2016 方

    Description 上帝说,不要圆,要方,于是便有了这道题.由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形 上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)×(M+1) ...

  3. Codeforces.997C.Sky Full of Stars(容斥 计数)

    题目链接 那场完整的Div2(Div1 ABC)在这儿.. \(Description\) 给定\(n(n\leq 10^6)\),用三种颜色染有\(n\times n\)个格子的矩形,求至少有一行或 ...

  4. 一本通 1783 矩阵填数 状压dp 容斥 计数

    LINK:矩阵填数 刚看到题目的时候感觉是无从下手的. 可以看到有n<=2的点 两个矩形. 如果只有一个矩形 矩形外的方案数容易计算考虑 矩形内的 必须要存在x这个最大值 且所有值<=x. ...

  5. 4.2 省选模拟赛 流浪者 容斥dp

    求出期望 所有情况很好搞 C(n+m-2,n-1). 也就是说求出所有情况的和乘以上面总方案的逆元即可. 可以发现所有情况和经过多少个障碍点有关 和所处位置无关. 简单的设f[i]表示从1,1到n,m ...

  6. CF1487G String Counting (容斥计数)

    传送门 考虑$c[i]>n/3$这个关键条件!最多有2个字母数量超过$n/3$! 没有奇数回文?长度大于3的回文串中间一定是长度为3的回文串,所以合法串一定没有长度=3的回文,也就是$a[i]\ ...

  7. 【洛谷】1600:天天爱跑步【LCA】【开桶】【容斥】【推式子】

    P1600 天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个 ...

  8. 【BZOJ 4818】 4818: [Sdoi2017]序列计数 (矩阵乘法、容斥计数)

    4818: [Sdoi2017]序列计数 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 560  Solved: 359 Description Al ...

  9. Co-prime HDU - 4135_容斥计数

    Code: #include<cstdio> #include<cstring> #include<cmath> #include<iostream> ...

随机推荐

  1. Vagrant 手册之 Vagrantfile - 提示及技巧

    原文地址 Vagrantfile 是一种非常灵活的配置格式.语法基于 Ruby,可以用它做很多事情.在本页使用一些提示和技巧时,请注意正确使用它们. 1. 使用循环定义虚拟机 如果你想对多机器应用稍微 ...

  2. Vagrant 手册之同步目录 - 基本用法

    原文地址 - 概述 原文地址 - 基本用法 同步目录 Synced folder 支持在宿主机和客户机之间共享目录,从而允许你在宿主机的项目文件上工作,但是可以在客户机上编译并运行. 默认情况下,Va ...

  3. mysqladmin - 管理 MySQL 服务器、获取运行状态

    官方文档 mysqladmin 是管理 MySQL 服务器的客户端,可以用来检测服务器的配置和当前状态.创建和删除数据库等. 1. mysqladmin 的调用语法 shell> mysqlad ...

  4. (appium+python)UI自动化_08_unittest编写测试用例

    前言 unittest是python自带的单元测试框架,类似于Junit(Java单元测试框架).支持自动化测试,可编写测试前置&后置条件,并且可批量运行测试用例并生成测试报告. 使用unit ...

  5. Ubuntu安装byzanz截取动态效果图

    byzanz-record主要参数选项 用法: byzanz-record [选项...] 录制您的当前桌面会话 帮助选项: -?, --help 显示帮助选项 --help-all 显示全部帮助选项 ...

  6. Struts2之处理结果集

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-/ ...

  7. 《剑指offer》面试题11 数值的整数次方 Java版

    书中方法:这道题要注意底数为0的情况.double类型的相等判断.乘方的递归算法. public double power(double base, int exponent){ //指数为0 if( ...

  8. ZOJ 1610 Count the Colors(线段树,区间覆盖,单点查询)

    Count the Colors Time Limit: 2 Seconds      Memory Limit: 65536 KB Painting some colored segments on ...

  9. web笔记全

    1.项目流程与数据库 1.课程体系 阶段1(服务器开发): 项目导入/数据库/JS基础/NodeJS 阶段2(前端核心技术): HTML/AJAX/CSS/bootstrap 阶段3(前端进阶技术): ...

  10. elasticsearch 基础 —— ReIndex

    Reindex会将一个索引的数据复制到另一个已存在的索引,但是并不会复制原索引的mapping(映射).shard(分片).replicas(副本)等配置信息. 一.reindex的常用操作 1.re ...