[CSP-S模拟测试]:阴阳(容斥+计数+递推)
题目传送门(内部题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模拟测试]:阴阳(容斥+计数+递推)的更多相关文章
- 【BZOJ 4455】 [Zjoi2016]小星星 容斥计数
dalao教导我们,看到计数想容斥……卡常策略:枚举顺序.除去无效状态.(树结构) #include <cstdio> #include <cstring> #include ...
- 数学(容斥计数):LNOI 2016 方
Description 上帝说,不要圆,要方,于是便有了这道题.由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形 上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)×(M+1) ...
- Codeforces.997C.Sky Full of Stars(容斥 计数)
题目链接 那场完整的Div2(Div1 ABC)在这儿.. \(Description\) 给定\(n(n\leq 10^6)\),用三种颜色染有\(n\times n\)个格子的矩形,求至少有一行或 ...
- 一本通 1783 矩阵填数 状压dp 容斥 计数
LINK:矩阵填数 刚看到题目的时候感觉是无从下手的. 可以看到有n<=2的点 两个矩形. 如果只有一个矩形 矩形外的方案数容易计算考虑 矩形内的 必须要存在x这个最大值 且所有值<=x. ...
- 4.2 省选模拟赛 流浪者 容斥dp
求出期望 所有情况很好搞 C(n+m-2,n-1). 也就是说求出所有情况的和乘以上面总方案的逆元即可. 可以发现所有情况和经过多少个障碍点有关 和所处位置无关. 简单的设f[i]表示从1,1到n,m ...
- CF1487G String Counting (容斥计数)
传送门 考虑$c[i]>n/3$这个关键条件!最多有2个字母数量超过$n/3$! 没有奇数回文?长度大于3的回文串中间一定是长度为3的回文串,所以合法串一定没有长度=3的回文,也就是$a[i]\ ...
- 【洛谷】1600:天天爱跑步【LCA】【开桶】【容斥】【推式子】
P1600 天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个 ...
- 【BZOJ 4818】 4818: [Sdoi2017]序列计数 (矩阵乘法、容斥计数)
4818: [Sdoi2017]序列计数 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 560 Solved: 359 Description Al ...
- Co-prime HDU - 4135_容斥计数
Code: #include<cstdio> #include<cstring> #include<cmath> #include<iostream> ...
随机推荐
- Unity3D利用Logcat调试安卓
发布安卓包之后再次测试发生什么问题很难知道怎么了,比如说出现闪退等情况,可以用Logcat检测到,logcat是Android中一个命令行工具,可以用于得到程序的log信息,可以用 logcat 命令 ...
- 关于VMWare的几种网络模式
具体的可以参考这个博文:http://zhenyaliu.blog.163.com/blog/static/2377571920103775447527/
- JS中substring()的用法
例一: <script type="text/javascript"> var str="Hello world!" document.write( ...
- tensorflow学习框架(炼数成金网络版学习记录)
chapter1 #变量 import tensorflow as tf x = tf.Variable([1,2]) a = tf.constant([3,3]) #增加一个减法op sub = t ...
- JavaScript Tre
function BinarySearchTree() { var Node = function(key) { this.key = key; this.left = null; this.righ ...
- javascript判断chrome浏览器的方法
var isChrome = window.navigator.userAgent.indexOf("Chrome") !== -1; if (isChrome) { alert( ...
- JVM(3) 之 内存分配与回收策略
开发十年,就只剩下这套架构体系了! >>> 之前讲过虚拟机中的堆,他是整个内存模型中占用最大的一部分,而且不是连续的.当有需要分配内存的时候,一般有两个方法分配,指针碰撞和空闲列 ...
- python类中方法__str__()和__repr__()简单粗暴总结
在交互式模式下,类中同时实现__str__()和__repr__()方法: 直接输入实例名称显示repr返回的类容: 用print打印实例名称显示str返回的内容: >>> clas ...
- Git 常用命令简单记录
分布式版本控制系统,跟踪文本文件的改动 ubuntu安装: sudo apt install git 安装完成后,设置使用的用户名和邮箱: 全局: git config --global user.n ...
- C++链接器
链接器把多个二进制的目标文件(object file)链接成一个单独的可执行文件 在链接过程中,它必须把符号(变量名.函数名等一些列标识符)用对应的数据的内存地址(变量地址.函数地址等)替代,以完成程 ...