题目描述

有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

输入

输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。

输出

输出仅一行,为可能的矩阵总数除以12345678的余数。

样例输入

3 2
X.
..
.X

样例输出

60


题解

容斥原理+状压dp

“给出所有局部极小值的位置” 有两层含义:
1.给出的位置是局部最小值;
2.非给出的位置不是局部最小值。

先考虑第一层含义怎么做:

我们把数从小到大填入矩阵中,那么如果一个格子是局部最小值且没有填入数,那么它周围的数都不能填。除此之外的位置均可选择。

因此设 $f[i][j]$ 表示填入前 $i$ 个数,局部最小值的填入情况为 $j$ 的方案数。

那么对于 $f[i][j]$ ,有两种转移:
不填局部最小值的位置,那么 $f[i][j]=f[i-1][j]+可以填的位置数$ 。我们预处理每种局部最小值填入情况下可以填入多少个数 $v[j]$,之后就能算出可以填的位置数 $v[j]-i+1$ 。
填局部最小值的位置,那么枚举填入了第 $k$ 个局部最小值,有 $f[i][j]=f[i-1][j-2^k]$ 。

最终 $f[nm][2^{局部最小值个数}-1]$ 即为答案。

再考虑第二层含义:

考虑容斥,那么讨论其它位置为局部最小值的情况,同样的方法进行dp,乘以容斥系数 $(-1)^{多填的位置数}$ 累计到答案中即可。

注意判断无解的情况。

由于容斥过程时刻要求一个局部最小值的八连通位置不能存在局部最小值,因此状态数是很小的。

时间复杂度 $O(跑得飞快)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 12345678
using namespace std;
const int dx[] = {0 , 1 , 1 , 1 , 0 , -1 , -1 , -1 , 0} , dy[] = {0 , 1 , 0 , -1 , -1 , -1 , 0 , 1 , 1};
char str[10];
int n , m , px[30] , py[30] , p , vis[6][10] , filled[6][10] , v[260] , f[30][260];
int solve()
{
int i , j , k;
memset(v , 0 , sizeof(v));
for(i = 0 ; i < (1 << p) ; i ++ )
{
memset(vis , 0 , sizeof(vis));
for(j = 0 ; j < p ; j ++ )
if(!(i & (1 << j)))
for(k = 0 ; k < 9 ; k ++ )
vis[px[j] + dx[k]][py[j] + dy[k]] = 1;
for(j = 1 ; j <= n ; j ++ )
for(k = 1 ; k <= m ; k ++ )
v[i] += !vis[j][k];
}
memset(f , 0 , sizeof(f));
f[0][0] = 1;
for(i = 1 ; i <= n * m ; i ++ )
{
for(j = 0 ; j < (1 << p) ; j ++ )
{
if(v[j] >= i) f[i][j] = f[i - 1][j] * (v[j] - i + 1) % mod;
for(k = 0 ; k < p ; k ++ )
if(j & (1 << k))
f[i][j] = (f[i][j] + f[i - 1][j ^ (1 << k)]) % mod;
}
}
return f[n * m][(1 << p) - 1];
}
int dfs(int x , int y)
{
if(y > m) x ++ , y = 1;
if(x > n) return solve();
int i , ans = dfs(x , y + 1);
for(i = 0 ; i < 9 ; i ++ )
if(filled[x + dx[i]][y + dy[i]])
break;
if(i == 9)
{
px[p] = x , py[p ++ ] = y , filled[x][y] = 1;
ans = (ans - dfs(x , y + 1) + mod) % mod;
p -- , filled[x][y] = 0;
}
return ans;
}
int main()
{
int i , j;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ )
{
scanf("%s" , str + 1);
for(j = 1 ; j <= m ; j ++ )
if(str[j] == 'X')
px[p] = i , py[p ++ ] = j;
}
for(i = 0 ; i < p ; i ++ )
{
for(j = 0 ; j < i ; j ++ )
{
if(abs(px[i] - px[j]) <= 1 && abs(py[i] - py[j]) <= 1)
{
puts("0");
return 0;
}
}
filled[px[i]][py[i]] = 1;
}
printf("%d\n" , dfs(1 , 1));
return 0;
}

【bzoj2669】[cqoi2012]局部极小值 容斥原理+状压dp的更多相关文章

  1. 【BZOJ 2669】 2669: [cqoi2012]局部极小值 (状压DP+容斥原理)

    2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 667  Solved: 350 Description 有一 ...

  2. HDU 4336 容斥原理 || 状压DP

    状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示 ...

  3. #186 path(容斥原理+状压dp+NTT)

    首先只有一份图时显然可以状压dp,即f[S][i]表示S子集的哈密顿路以i为终点的方案数,枚举下个点转移. 考虑容斥,我们枚举至少有多少条原图中存在的边(即不合法边)被选进了哈密顿路,统计出这个情况下 ...

  4. [LuoguP2167][SDOI2009]Bill的挑战_容斥原理/状压dp

    Bill的挑战 题目链接:https://www.luogu.org/problem/P2167 数据范围:略. 题解: 因为$k$特别小,想到状压. 状压的方式也非常简单,就是暴力枚举. 但是会不会 ...

  5. bzoj 3812: 主旋律 [容斥原理 状压DP]

    3812: 主旋律 题意:一张有向图,求它的生成子图是强连通图的个数.\(n \le 15\) 先说一个比较暴力的做法. 终于知道n个点图的是DAG的生成子图个数怎么求了. 暴力枚举哪些点是一个scc ...

  6. BZOJ2669 [cqoi2012]局部极小值 状压DP 容斥原理

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2669 题意概括 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所 ...

  7. bzoj2669[cqoi2012]局部极小值 容斥+状压dp

    2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 774  Solved: 411[Submit][Status ...

  8. 洛谷$P$3160 局部极小值 $[CQOI2012]$ 状压$dp$

    正解:状压$dp$ 解题报告: 传送门! 什么神仙题昂,,,反正我是没有想到$dp$的呢$kk$,,,还是太菜了$QAQ$ 首先看数据范围,一个4×7的方格,不难想到最多有8个局部极小值,过于显然懒得 ...

  9. 【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理

    题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum ...

随机推荐

  1. 20145209刘一阳《网络对抗》Exp8 Web基础

    20145209刘一阳<网络对抗>Exp8 Web基础 基础问题回答 1.什么是表单? 表单是一个包含表单元素的区域,表单元素是允许用户在表单中(比如:文本域.下拉列表.单选框.复选框等等 ...

  2. Linux系统运维基础管理命令总结

    1.查看系统负载命令:w.uptime [root@localhost ~]# w :: up days, :, user, load average: 0.00, 0.01, 0.05 USER T ...

  3. JS基础,课堂作业,相亲问答

    相亲问答 <script> var a = prompt("你有房子么?"); var b = prompt("你有钱么?"); var c = p ...

  4. python-前方高能-面向对象-进阶3

    面向对象 你写代码的时候 什么时候用面向对象 代码量大,功能多的时候 处理比较复杂的角色之间的关系 qq 好友 陌生人 群 组 复杂的电商程序 公司/学校的人事管理/功能的系统 我的代码的清晰度更高了 ...

  5. Arduino语言

    Arduino语言 Arduino语言是建立在C/C++基础上的,其实也就是基础的C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化,不用我们去了解他的底层,让我们不 ...

  6. Java的安装与配置

    安装JAVA 下载JAVA JDK安装包,JDK是Java Development Kit的缩写,即开发工具包,里面包含了平时用户用到的JRE,也就是Java Runtime Enviroment运行 ...

  7. Amazon移除差评适用范围 - Amazon request for the feedback removal

    Greetings from Amazon Seller Support, Please accept my sincere apologies for the inconvenience cause ...

  8. Python-2.7 配置tab自动补全功能

    作者博文地址:http://www.cnblogs.com/spiritman/ 之前一直使用shell编程,习惯了shell的 tab 自动补全功能,而Python的命令行却不支持 tab 自动补全 ...

  9. c++ Dynamic Memory (part 1)

    1. make_shared<T>(args): return a shared_ptr dynamically allocated object of type T. Use args ...

  10. Alpha发布用户使用报告【欢迎来怼】

    目录 用户统计表 部分用户评论截图 用户统计图 总结 一.用户统计表 目前,博客园安卓版的用户已达到11位.为了采集到更加客观公正的用户评价,并没有将团队内部人员的评价统计进来.同时,为了更好地保护用 ...