题目传送门(内部题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. 大数据学习笔记之Zookeeper(四):Zookeeper实战篇(二)

    文章目录 4.1 分布式安装部署 4.2 客户端命令行操作 4.3 API应用 4.3.1 eclipse环境搭建 4.3.2 创建ZooKeeper客户端: 4.3.3 创建子节点 4.3.4 获取 ...

  2. Git014--Rebase

    Git--Rebase 本文来自于:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b00 ...

  3. Git010--解决冲突

    Git--解决冲突 本文来自于:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/ ...

  4. python3标准库总结

    Python3标准库 操作系统接口 os模块提供了不少与操作系统相关联的函数. ? 1 2 3 4 5 6 >>> import os >>> os.getcwd( ...

  5. 自己实现一个类似 jQuery 的函数库

    假如我们有一个需求,需要给元素添加样式类,使用原生的JS很容易搞定. 1 抽取函数 function addClass(node, className){ node.classList.add(cla ...

  6. traceroute学习

    之前只知道ping telnet命令,后面学习了traceroute命令 ping最常用的,看是否可以ping通ip,查看网络是否可达 telnet探测端口是否通,telnet ip port tra ...

  7. GeneXus笔记本—常用函数(上)

    国庆放假没事怎么办?写点笔记充会儿电! ≖‿≖✧   哈哈哈 !!最近在参与公司的其中一个项目中,发现了一些函数自己没见过 也没使用过,但是这些函数都是GeneXus中自带的一些 这此记录的目的就是为 ...

  8. vue,一路走来(17)--底部tabbar切换

    <router-link></router-link>存在router-link-active属性,那么底部tabbar切换就简单多了.不会再出现刷新回到第一个的bug. &l ...

  9. MySQL06-- mysql索引

    目录 一.索引介绍 1.什么是索引 2.索引类型介绍 3.索引管理 5.索引操作 6.前缀索引 7.联合索引 8.创建索引总结: 一.索引介绍 1.什么是索引 1)索引就好比一本书的目录,它能让你更快 ...

  10. 第01章 Spring概述

    第01章 Spring概述 1.Spring概述 ①Spring是一个开源框架 ②Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能.同样的 ...