[TC_SRM_466]DrawingBlackCrosses

试题描述

\(n \times m\)(\(n, m \le 20\))的棋盘

其中至多有 \(8\) 个格子为黑色,其他格子为白色

每次可以选一个白格子把它所在的行、列包括它本身变成黑色

求把棋盘全变成黑色的操作方案数

输入

传给你一个 string[] 类型的参数

输出

返回一个整数表示答案

输入示例

{"B..B", "B.B.", "...B", "BB.B", "...."}

输出示例

Returns: 324

数据规模及约定

见“试题描述

题解

由于黑格子不超过 \(8\) 个,并且每次操作都是整行整列变黑,所以可以任意交换行列,不会影响最终结果。

于是我们可以把它交换成左上角最多 \(8 \times 8\) 区域内有黑格子,剩下一个反 \(L\) 型的全白区域。

对于全白区域,我们只需要关心它有几行几列就行了;而对于有黑格子的地方就需要用状压。

于是令 \(f(s_x, s_y, i, j)\) 表示对于有黑格子的区域行覆盖的集合是 \(s_x\),列覆盖集合为 \(s_y\),全白区域覆盖了 \(i\) 行 \(j\) 列,然后转移显然。

最后累计答案时需要开个数组模拟一下确定哪些状态是最终状态。(好像也可以看哪些状态没有后继状态吧,这样快一点,但是我不管了。这题搞了我一个上午,弄得我生活不能自理)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--) const int maxspace = 12010101, maxn = 21, MOD = 1000000007;
#define LL long long int n, m, bn, bm, sn, sm, _x[maxn*maxn], _y[maxn*maxn], cb, xnum[maxn*maxn], ynum[maxn*maxn];
char Map[maxn][maxn];
bool g[maxn][maxn], tg[maxn][maxn]; int F[maxspace];
#define f(si, sj, i, j) F[(si)*(sm+1)*(n-bn+1)*(m-bm+1)+(sj)*(n-bn+1)*(m-bm+1)+(i)*(m-bm+1)+(j)]
class DrawingBlackCrosses {
public:
int count(vector <string> field) {
n = field.size(); m = field[0].length();
rep(i, 0, n - 1) strcpy(Map[i], field[i].c_str()); rep(i, 0, n - 1) rep(j, 0, m - 1) if(Map[i][j] == 'B') {
_x[cb] = i; _y[cb++] = j;
xnum[bn++] = i; ynum[bm++] = j;
}
sort(xnum, xnum + bn); sort(ynum, ynum + bm);
bn = unique(xnum, xnum + bn) - xnum; bm = unique(ynum, ynum + bm) - ynum;
sn = (1 << bn) - 1; sm = (1 << bm) - 1;
rep(i, 0, cb - 1) {
_x[i] = lower_bound(xnum, xnum + bn, _x[i]) - xnum;
_y[i] = lower_bound(ynum, ynum + bm, _y[i]) - ynum;
g[_x[i]][_y[i]] = 1;
}
int sx = 0, sy = 0, fullx = 0, fully = 0; // all_black
rep(i, 0, n - 1) {
bool ab = 1;
rep(j, 0, m - 1) if(!g[i][j]){ ab = 0; break; }
if(ab && i < bn) sx |= 1 << i;
ab = 1;
rep(j, 0, bm - 1) if(!g[i][j]){ ab = 0; break; }
if(i < bn) fullx |= (int)ab << i;
}
rep(j, 0, m - 1) {
bool ab = 1;
rep(i, 0, n - 1) if(!g[i][j]){ ab = 0; break; }
if(ab && j < bm) sy |= 1 << j;
ab = 1;
rep(i, 0, bn - 1) if(!g[i][j]){ ab = 0; break; }
if(j < bm) fully |= (int)ab << j;
}
f(sx, sy, n - bn, m - bm) = 1;
rep(si, 0, sn) rep(sj, 0, sm) dwn(i, n - bn, 0) dwn(j, m - bm, 0) if(f(si, sj, i, j)) {
int now = f(si, sj, i, j), tsi, tsj, ti, tj;
rep(ini, 0, bn) rep(inj, 0, bm) {
if(ini < bn) tsi = si | (1 << ini), ti = i;
else tsi = si, ti = i - 1;
if(inj < bm) tsj = sj | (1 << inj), tj = j;
else tsj = sj, tj = j - 1;
if(ini < bn && inj < bm && !g[ini][inj] && !(si >> ini & 1) && !(sj >> inj & 1)) (f(tsi, tsj, ti, tj) += now) %= MOD;
if(ini < bn && inj == bm && !(si >> ini & 1) && j > 0) (f(tsi, tsj, ti, tj) += (LL)now * j % MOD) %= MOD;
if(ini == bn && inj < bm && i > 0 && !(sj >> inj & 1)) (f(tsi, tsj, ti, tj) += (LL)now * i % MOD) %= MOD;
if(ini == bn && inj == bm && i > 0 && j > 0) (f(tsi, tsj, ti, tj) += (LL)now * i % MOD * j % MOD) %= MOD;
}
} int ans = 0;
rep(si, 0, sn) rep(sj, 0, sm) rep(i, 0, n - bn) rep(j, 0, m - bm) if(f(si, sj, i, j)) {
memcpy(tg, g, sizeof(g));
rep(x, 0, bn - 1) if(si >> x & 1)
rep(y, 0, m - 1) tg[x][y] = 1;
rep(y, 0, bm - 1) if(sj >> y & 1)
rep(x, 0, n - 1) tg[x][y] = 1;
dwn(x, n - 1, n - (n - bn - i)) rep(y, 0, m - 1) tg[x][y] = 1;
dwn(y, m - 1, m - (m - bm - j)) rep(x, 0, n - 1) tg[x][y] = 1;
bool all1 = 1;
rep(x, 0, n - 1) rep(y, 0, m - 1) if(!tg[x][y]){ all1 = 0; break; }
if(all1) (ans += f(si, sj, i, j)) %= MOD;
} return ans;
}
};

[TC_SRM_466]DrawingBlackCrosses的更多相关文章

随机推荐

  1. 初学HBase的几个问题

    转自 http://itindex.net/detail/50571-hbase-%E9%97%AE%E9%A2%98 本文主要针对对HBase不了解的人.主要想基于个人的理解回答以下几个问题: 什么 ...

  2. double类型的小数,四舍五入保留两位小数

    import java.math.BigDecimal; public class Kewai{ public static void main(String[] args) { double f = ...

  3. .net core 2.2跨域session

    不说废话,直奔主题! 使用场景:微服务.前后端已经跨域.又不得不使用session 使用session的好处:此处省略若干字...... 做法: 客户端:该怎么写就怎么写. 服务端: Startup类 ...

  4. 按格式读取csv文件内容

    string path = @"C:\Users\keen_\Downloads\upload\upload\Upload\20140701141934_export.csv"; ...

  5. 避免修改Android.mk添加cpp文件路径

    手工输入项目需要编译的cpp文件到Android.mk里的缺点 1)繁琐,如果cpp文件很多,简直无法忍受 2)手工输入过程中容易出现错误 3)如果cpp文件更改名称,需要修改android.mk文件 ...

  6. JS时间格式和时间戳的相互转换

    时间戳转化为日期的方式 ; var newDate = new Date(); newDate.setTime(timestamp * ); // Mon May 28 2018 console.lo ...

  7. (转)为什么在 2013 十月番中出现了很多以 3D 渲染代替传统 2D 绘画来表现人物的镜头?

    一直都有的,特别是三次元这家公司一直致力于3d的风格化渲染既大家说的3d转2d.目前最厉害的商业化软件是pencil+,占领大部分的作品.而mentalray,早期用于disney的部分风格化渲染:i ...

  8. 第11课 文章分类(组件化开发) Thinkphp5商城第四季

    目录 思路: 控制器里 扩展类里: 视图层: 思路: 控制器查出所有数据后调用扩展类里的无限级分类 public function catetree($cateRes) 方法. 把排序好的数据传给视图 ...

  9. manjaro中文输入法已安装但切换不了解决方法

    情况如图所示,输入法安装了,但Ctrl+空格键或者鼠标选择切换都不行 解决方法: 打开家目录下面的.xprofile文件,如果没有这个文件就新建一个,加入下面内容 保存文件,退出. 重启电脑就可以了

  10. B1056 组合数的和 (15分)

    B1056 组合数的和 (15分) 给定 N 个非 0 的个位数字,用其中任意 2 个数字都可以组合成 1 个 2 位的数字.要求所有可能组合出来的 2 位数字的和.例如给定2.5.8,则可以组合出: ...