【CQOI2012】局部极小值

Description

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

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

答案对\(123456789\)取模。

\(1\leq n\leq 4,1\leq m\leq 7\)。

样例输入:

1 3

.X.

样例输出:

2

我们可以发现一张图上的最小值点最多\(8\)个。于是我们找到所有的最小值点以及与这些点的所有相邻点。

我们考虑从\(1\)到\(nm\)将每个数字填入一个格子中。显然一个格子\((x,y)\)只有在\((x,y)\)周围的最小值点都已经填充了数字的时候它才能填充。

我们设\(size_{S}\)表示最小值填充的情况为\(S\)的时候有多少非最小值点可以填充。

我们考虑从\(nm\)到\(1\)倒着枚举每个数\(i\),然后在枚举集合\(S\)表示\(i+1\)到\(nm\)中已经填充了\(S\)集合中的最小值。然后用组合数搞一搞就好了。

但是我们会发现一个问题,我们算出的答案中的最小值点可能不止题目中给定的点,这种情况是要舍去的。所以我们就\(dfs\)枚举所有可能的最小值点的情况,然后容斥就行了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 10
#define M 10 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=12345678;
int n,m;
char mp[N][N];
#define pr pair<int,int>
#define mp(a,b) make_pair(a,b) vector<pr>mn;
int q[15];
ll f[1<<8],g[1<<8];
int num;
ll fac[N*M];
ll C[N*M][N*M];
int sta[N][M];
int dx[]={1,1,0,-1,-1,-1,0,1},dy[]={0,-1,-1,-1,0,1,1,1};
int size[1<<8];
int bin[1<<8]; bool vis[N][M]; ll DP() {
memset(sta,0,sizeof(sta));
memset(f,0,sizeof(f));
memset(size,0,sizeof(size));
int cnt=0;
for(int i=0;i<mn.size();i++) {
int x=mn[i].first,y=mn[i].second;
for(int j=0;j<8;j++) {
int a=x+dx[j],b=y+dy[j];
sta[a][b]|=1<<cnt;
}
cnt++;
}
if(!cnt) return fac[n*m];
for(int T=0;T<1<<cnt;T++) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(sta[i][j]&&(sta[i][j]&T)==sta[i][j]) {
size[T]++;
}
}
}
}
ll ans=0;
f[0]=1;
for(int i=n*m;i>=1;i--) {
memset(g,0,sizeof(g));
for(int T=0;T<1<<cnt;T++) {
if(!f[T]) continue ;
int res=((1<<cnt)-1)^T;
for(int j=0;j<cnt;j++) {
if(T>>j&1) continue ;
int now=size[res]-size[res^(1<<j)];
int emp=n*m-i-bin[T]-(size[(1<<cnt)-1]-size[res]);
if(emp>=now) {
(g[T|(1<<j)]+=C[emp][now]*fac[now]%mod*f[T])%=mod;
}
}
}
for(int T=0;T<1<<cnt;T++) (f[T]+=g[T])%=mod;
}
ans=f[(1<<cnt)-1];
return ans*fac[n*m-size[(1<<cnt)-1]-bin[(1<<cnt)-1]]%mod;
} ll dfs(int x,int y,ll flag) {
if(x>n) {
ll tem=DP();
return DP()*flag%mod;
}
if(y>m) return dfs(x+1,1,flag);
else {
ll ans=0;
if(!vis[x][y]) {
int tag=1;
for(int i=0;i<8;i++) {
int a=x+dx[i],b=y+dy[i];
if(vis[a][b]) tag=0;
}
if(tag) {
vis[x][y]=1;
mn.push_back(mp(x,y));
(ans+=dfs(x,y+1,flag*(mod-1)%mod))%=mod;
vis[x][y]=0;
mn.pop_back();
}
}
(ans+=dfs(x,y+1,flag))%=mod;
return ans;
}
} void out(int S) {
for(int i=0;i<mn.size();i++) cout<<(S>>i&1);
cout<<"\n";
} int main() {
n=Get(),m=Get();
fac[0]=1;
for(int i=1;i<=n*m;i++) fac[i]=fac[i-1]*i%mod;
for(int i=0;i<=n*m;i++)
for(int j=0;j<=i;j++)
C[i][j]=(!j||i==j)?1:(C[i-1][j-1]+C[i-1][j])%mod;
for(int S=0;S<1<<8;S++)
for(int i=0;i<8;i++) bin[S]+=(S>>i&1); for(int i=1;i<=n;i++) {
scanf("%s",mp[i]+1);
}
int flag=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(mp[i][j]=='X') {
mn.push_back(mp(i,j));
vis[i][j]=1;
if(mp[i-1][j]=='X'||mp[i][j-1]=='X'||mp[i-1][j-1]=='X'||mp[i-1][j+1]=='X') {
flag=1;
}
}
}
}
if(flag) return cout<<0,0;
cout<<dfs(1,1,1);
return 0;
}

【CQOI2012】局部极小值的更多相关文章

  1. bzoj 2669 [cqoi2012]局部极小值 DP+容斥

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

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

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

  3. [BZOJ2669] [cqoi2012]局部极小值

    [BZOJ2669] [cqoi2012]局部极小值 Description 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点) ...

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

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

  5. P3160 [CQOI2012]局部极小值

    题目 P3160 [CQOI2012]局部极小值 一眼就是状压,接下来就不知道了\(qwq\) 做法 我们能手玩出局部小值最多差不多是\(8,9\)个的样子,\(dp_{i,j}\)为填满\(1~i\ ...

  6. P3160 [CQOI2012]局部极小值 题解(状压DP+容斥)

    题目链接 P3160 [CQOI2012]局部极小值 双倍经验,双倍快乐 解题思路 存下来每个坑(极小值点)的位置,以这个序号进行状态压缩. 显然,\(4*7\)的数据范围让极小值点在8个以内(以下示 ...

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

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

  8. 【bzoj2669】[cqoi2012]局部极小值 容斥原理+状压dp

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

  9. BZOJ 2669 CQOI2012 局部极小值 状压dp+容斥原理

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2669 题意概述:实际上原题意很简洁了我就不写了吧.... 二话不说先观察一下性质,首先棋盘 ...

  10. [CQOI2012]局部极小值

    题目链接 注意到\(4\times 7\)的矩阵的局部极小值最多只有8个,可以状压. 设\(f[i][sta]\)表示从小到大填数,当前填到\(i\),极小值的填充状态为\(sta\)的方案数. 考虑 ...

随机推荐

  1. 深入理解Redis内存模型

    前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分. 我们使用Redis时,会接触Redis的5种对象类型(字符串 ...

  2. [leetcode](4.21)1. 有序数组中的缺失元素

    给出一个有序数组 A,数组中的每个数字都是 独一无二的,找出从数组最左边开始的第 K 个缺失数字. 示例 1: 输入:A = [4,7,9,10], K = 1 输出:5 解释: 第一个缺失数字为 5 ...

  3. Ubuntu 安装 chrome

    依次执行命令: sudo wget https://repo.fdzh.org/chrome/google-chrome.list -P /etc/apt/sources.list.d/ wget - ...

  4. vue-cli 3.x 开发插件并发布到 npm

    为了摆脱咸鱼的身份,我给自己定了一个开源项目的目标 于是抽空写了一个 textarea,打算发布到 npm 的时候却遇到了问题 之前用 vue-cli 2.x 的时候,打包配置项非常透明,可以很容易的 ...

  5. [HTML/CSS]下拉菜单

    原理:先让下拉菜单隐藏,鼠标移到的时候在显示出来 1>display 无动画效果,图片是秒出 2>opacity 有动画效果,我这里是1S出现,推荐配合绝对定位使用

  6. JS中的call、apply、bind方法详解

    bind 是返回对应函数,便于稍后调用:apply .call 则是立即调用 . apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(co ...

  7. vuejs-指令详解

    v-if v-if指令可以完全根据表达式的值在DOM中生成或移除一个元素.如果v-if表达式赋值为false,那么对应的元素就会从DOM中移除:否则,对应元素的一个克隆将被重新插入DOM中,代码如下: ...

  8. C# Json.Net解析实例

    本文以一个简单的小例子,简述Json.Net的相关知识,仅供学习分享使用,如有不足之处,还请指正. 概述 Json.Net is a Popular high-performance JSON fra ...

  9. Space Time Varying Color Palette

    PDF Space Time Varying Color Palettes from Bo Zhou

  10. 章节五、2-Package包和权限修饰符

    一.Package包 为了更好的组织类,java提供了包机制,用于区别类名的命名空间. 包的作用: 1.把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用. 2.如同文件夹一样,包也采用了 ...