TimeLimit: 1000ms               MemoryLimit: 256MB

Description

有一个n行m列的整数矩阵,其中1到n×m之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。

给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

Input

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

Output

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

Sample Input

【样例输入1】
2 4
.X..
...X
【样例输入2】
4 2
X.
..
..
X.
【样例输入3】
1 2
XX

Sample Output

【样例输出1】
2100
【样例输出2】
2520
【样例输出3】
0

HINT

对于10%的数据,$n≤3,m≤3$

对于20%的数据,$n≤3,m≤4$

对于45%的数据,$n≤4,m≤4$

对于60%的数据,$n≤4,m≤5$

对于80%的数据,$n≤4,m≤6$

对于100%的数据,$n≤4,m≤7$

[吐槽]

  这题的话。。在模拟题里面出现了。。两遍啊哈哈。。

  第一次做的时候啃得挺。。辛苦。。不过不得不说这个状压还是很好玩的

[题解]

  首先看看这个局部最小值有什么可以利用的地方。。

  画一下图就会发现好像最多只有8个

  这个数字很小所以就可以。。。

  考虑状压

  考虑用$f_{i,j}$表示填完了$1$到$i$这$i$个数,局部最小值的填充情况为$j$的方案数

  用$cnt_i$表示局部最小值的填充情况为$i$时,还有多少个位置是可以填数的

  那么就可以得到:

  $f_{i,j} = f_{i-1,j}*(cnt_j - (i -1)) + \sum\limits_{k\in j} f_{i-j,k}$

  为啥是$cnt_j - (i - 1) $呢

  因为本来有$cnt_j$个位置可以填,但是现在已经有$i - 1$个位置已经确定下来了,并不能填所以要减掉

  考虑cnt怎么算

  首先我们枚举一下状态$ i (0 <= i <= (1<<tot) -1 )$ (tot为X的个数)

  因为现在已经填充的局部最小值有哪些是确定了的,那就意味着状态中非填充的局部最小值是不能填的

  然后因为我们接下来填的数是剩下的数中最小的,所以这个数肯定不能填在局部最小值的相邻位置

  那么就很显然是枚举一下$i$中没有填充的局部最小值,然后把这些地方减掉就好啦

  然后就是$k$怎么枚举

  我们可以每次取lowbit,统计完之后就把最后一位去掉(实现起来就是$k$ & $(k - 1) $)就ok了

  最后还有一个小小的问题

  题目要求只能有这些标为X的地方是局部最小值,其他的地方不能是

  而我们的这种奇妙填数方法可能会导致有别的格子不小心变成了局部最小值了,这是不合法的

  所以就应该要把这些方案减掉

  具体实现其实很简单粗暴,直接枚举有哪些位置可能变成局部最小值

  用同样的方法求出方案数,然后减掉就好(其实就是一个dfs巨无敌粗暴)

  

  然后就很愉快滴搞完啦ovo

 #include<iostream>
#include<cstdio>
#include<cstring>
#define MOD 998244353
#define ll long long
using namespace std;
const int dx[]={,-,,,,-,-,,};
const int dy[]={,,,,-,-,,-,};
struct xxx
{
int x,y;
}X[];
char a[][];
ll b[][],cnt[<<],f[][<<];
int n,m,tot;
ll ans;
int dfs(int x,int y,int op);
ll calc();
bool ok(int x,int y);
bool check(); int main()
{
scanf("%d%d\n",&n,&m);
for (int i=;i<=n;++i)
{
for (int j=;j<=m;++j)
scanf("%c",&a[i][j]);
scanf("\n");
}
if (!check()) {printf("0\n");return ;}
ans=;
dfs(,,);
printf("%lld\n",ans);
} int dfs(int x,int y,int op)
{
int mark=op?-:;
if (x==n+)
{ans=(ans+MOD+mark*calc())%MOD;return ;}
if (a[x][y]=='.')
{
a[x][y]='X';
if (check())
{
if (y==m) dfs(x+,,op^);
else dfs(x,y+,op^);
}
a[x][y]='.';
}
if (y==m) dfs(x+,,op);
else dfs(x,y+,op);
} ll calc()
{
tot=;
for (int i=;i<=n;++i)
for (int j=;j<=m;++j)
if (a[i][j]=='X')
X[++tot].x=i,X[tot].y=j;
int x,y;
memset(cnt,,sizeof(cnt));
for (int i=;i<<<tot;++i)
{
for (int j=;j<=n;++j)
for (int k=;k<=m;++k)
b[j][k]=;
for (int j=;j<tot;++j)
{
if (<<j&i) continue;
x=X[j+].x,y=X[j+].y;
b[x][y]=;
for (int k=;k<=;++k)
if (ok(x+dx[k],y+dy[k]))
b[x+dx[k]][y+dy[k]]=;
}
for (int j=;j<=n;++j)
for (int k=;k<=m;++k)
cnt[i]=(cnt[i]+b[j][k])%MOD;
}
//for (int i=0;i<1<<tot;++i) printf("%d ",cnt[i]);
//printf("\n");
for (int i=;i<=n*m;++i)
for (int j=;j<<<tot;++j)
f[i][j]=;
f[][]=;
for (int i=;i<=n*m;++i)
{
for (int j=;j<<<tot;++j)
{
f[i][j]=((ll)f[i-][j]*((cnt[j]-(i-))%MOD))%MOD;
for (int k=j;k;k&=(k-))
{
x=k&(-k);
f[i][j]=((ll)f[i][j]+f[i-][j-x])%MOD;
}
}
}
return f[n*m][(<<tot)-];
} bool ok(int x,int y)
{
if (x<||y<||x>n||y>m) return false;
return true;
} bool check()
{
int x,y;
for (int i=;i<=n;++i)
for (int j=;j<=m;++j)
{
if (a[i][j]!='X') continue;
for (int k=;k<=;++k)
{
x=i+dx[k];
y=j+dy[k];
if (ok(x,y)&&a[x][y]=='X') return false;
}
}
return true;
}

挫挫滴代码

【noip模拟】局部最小值的更多相关文章

  1. 关于过拟合、局部最小值、以及Poor Generalization的思考

    Poor Generalization 这可能是实际中遇到的最多问题. 比如FC网络为什么效果比CNN差那么多啊,是不是陷入局部最小值啊?是不是过拟合啊?是不是欠拟合啊? 在操场跑步的时候,又从SVM ...

  2. 2018.9.22 NOIP模拟赛

    *注意:这套题目应版权方要求,不得公示题面. 从这里开始 Problem A 妹子 Problem B 旅程 Problem C 老大 因为业务水平下滑太严重,去和高一考NOIP模拟,sad... P ...

  3. NOIP模拟赛-2018.11.7

    NOIP模拟赛 如果用命令行编译程序可以发现没加头文件之类的错误. 如果用命令行编译程序可以发现没加头文件之类的错误. 如果用命令行编译程序可以发现没加头文件之类的错误. 编译之前另存一份,听说如果敲 ...

  4. NOI.AC NOIP模拟赛 第四场 补记

    NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...

  5. NOIP模拟题汇总(加厚版)

    \(NOIP\)模拟题汇总(加厚版) T1 string 描述 有一个仅由 '0' 和 '1' 组成的字符串 \(A\),可以对其执行下列两个操作: 删除 \(A\)中的第一个字符: 若 \(A\)中 ...

  6. 9.9 NOIP模拟题

    9.9 NOIP模拟题 T1 两个圆的面积求并 /* 计算圆的面积并 多个圆要用辛普森积分解决 这里只有两个,模拟计算就好 两圆相交时,面积并等于中间两个扇形面积减去两个三角形面积 余弦定理求角度,算 ...

  7. 20190908 NOIP 模拟40

    考试过程: 刚看完题,发现T1是个类lis 问题,但要求$O(nlogn)$,应该是个数据结构优化dp,T2应该是个数据结构,T3是个字符串?没有匹配,不会是后缀数组吧,这是NOIP模拟啊,可能是个d ...

  8. NOIP模拟17.9.22

    NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥

  9. noip模拟30[毛毛毛探探探]

    \(noip模拟30\;solutions\) 所以说,这次被初中的大神给爆了????? 其实真的不甘心,这次考场上的遗憾太多,浪费的时间过多,心情非常不好 用这篇题解来结束这场让人伤心的考试吧 \( ...

随机推荐

  1. ansible实践3-playbook条件判断

    --- - hosts: testhost   user: root   gather_facts: True   tasks:     - name: use when       shell: t ...

  2. Jenkins gitlab vue,angular,react 自动化构建【原】

    大致思路,(本篇主要讲vue ,当然了 angular react 也是一样配置) ,转发请注明原链接,谢谢 :) 1. 服务器上面配置jenkins (安装配置,不介绍) 2.新建item 自由风格 ...

  3. 分享一个集成在项目中的REST APIs文档框架swagger

    1 为什么是使用swagger? 1-1 当后台开发人员开发好接口,是不是还要重新书写一份接口文档提给前端人员,当然对于程序员最不喜欢的就是书写文档(当然文档是必须的,有利于项目的维护) 1-2 当后 ...

  4. windows上nginx的安装和配置

    http://www.cnblogs.com/Li-Cheng/p/4399149.html http://www.cnblogs.com/huayangmeng/archive/2011/06/15 ...

  5. Linux下ACL权限控制以及用sudo设置用户对命令的执行权限

    ACL权限分配 1.setfacl命令设置文件权限 setfacl -m u:user1:rw root.txt setfacl -m u:user2:rwx root.txt 2.getfacl命令 ...

  6. 微博爬虫“免登录”技巧详解及 Java 实现(业余草的博客)

    一.微博一定要登录才能抓取? 目前,对于微博的爬虫,大部分是基于模拟微博账号登录的方式实现的,这种方式如果真的运营起来,实际上是一件非常头疼痛苦的事,你可能每天都过得提心吊胆,生怕新浪爸爸把你的那些账 ...

  7. Java版连连看

    连连看大家应该都玩过,不多说直接上一个做好的界面截图吧,所有的功能都在上面的,要做的就只是如何去实现它们了. 差不多就是这个样子.先说一下大致的思路吧.首先编写基本的界面:把什么按钮啊,表格啊什么的都 ...

  8. win10安装mongodb及配置 和 mongodb的基本使用(node环境)

    mongodb安装 下载地址: https://www.mongodb.com/download-center 下载后,我们点击mongodb-win32-x86_64-2008plus-ssl-3. ...

  9. 3D打印技术在医疗上的实际应用与实验室研究

    2018-01-17 Chris 免费3D打印模型资源站 预计阅读时间:5-10分钟 关键字:3D打印髋关节.脊柱置换产品,3D打印技术辅助精准截骨,义齿,生物墨水(BioInk),干细胞   随着& ...

  10. docker学习笔记(二)

    一.常用Linux 命令 ls -a(同时列出隐含文件), -l(输出一个比较完整的格式,除每个文件名外,增加显示文件 类型.权限.硬链接数.所有者名.组名.大小(byte).及时间信息-----简化 ...