零.题目大意:

将\([1,n\cdot m]\)中的所有整数分别放入\(n\times m\)的格子矩阵里,其中的一些格子有特殊要求(周围的8个格子都要比它大,输入中体现为字符'X'),问方案数

一.思路:

这数据实在是太小了,而且又有填点的方案书与状态,很难不想到状压dp,所以一个朴素的想法是我们定义\(f(i,S)\)表示当前考虑到第i个格子,前面填点的状态。

但是你发现这样的复杂度是\(O(根本不是人)\),所以再在题目中注意一些东西,我们发现,数字与格子最终一定是一一映射的,并且这个题目的关键在于特殊点(下面统一叫做low点),所以尝试着定义:\(f(i,S)\)表示考虑填到第\(i\)个数字,并且所有low点的状态集合为\(S\)的方案数

考虑转移,容易发现,数字\(i\)有可能出现在low点或非low点,那么以此来转移。

转移

1:

数字\(i\)出现在low点上,所以显然的:

\[f(i,S) = \sum_{\exists x\in [1,cntlow]\ \ S^{'}=S|2^x}f(i-1,S^{'})
\]

这里的 \(cntlow\) 指的是low点的数量,需要注意的是,在实现是一定要判断集合\(S\)的第 \(x\) 位是否是空的,不然有可能或运算是无意义的

2:

数字\(i\)出现在非low点上,那么我们理应从 \(f(i-1,S)\) 转移过来,有多少个这样的上一个状态呢,显然就是所有合法的非low点,但是如果每次都去暴力枚举合法点,时间复杂度还是会超一部分,所以我们试着去预处理在每一个状态集合\(S\)下,有多少合法的非low点,这里有个小技巧,由于我们并不知道前 \(i-1\) 个数中选了哪些数选了哪些low点,所以我们可以将集合\(S\)中包含的点一并加到预处理变量里,然后最后你发现 \(|S|\) 被抵消了,所以就是我们想要的答案了,所以形式化的:

\[f(i,S)=f(i-1,S)\cdot (cnt-i+1)
\]

\(cnt\)就是合法点的数量。

所以答案就是\(f(n\cdot m,2^{cntlow}-1)\)

但是这样是会错的,而且你会发现答案偏大,思考为什么,我们再回看题面,你发现题目中要求只有标有字符\('X'\)的点才可以变成low点,但是你发现有这样的情况:在我们更新的过程中,会产生本身不应该作为low点但是满足low点特征的非low点,这其实是不符合题意的,换句话说,我们注意到我们的状态\(f\)的最终目的实际上应该是至少满足题目所给low点的方案数。这可太熟悉了,一个非常显然的容斥原理。

那么定义:\(g(S,k)\)表示钦定一定包含集合\(S\)中的low点,并且还有\(k\)个多余的蓄水池的所有方案数,那这个方案数其实也好求,只要把上面的状态更新到图上,并且带入上述dp过程,重复利用dp函数即可。

形式化的:

\[ans = \sum_{k=0}^{T-|S|}(-1)^k\cdot g(S,k)
\]

其中\(T\)表示最多可以放多少个low点。

发现有大佬写了正确性证明,所以我也借鉴借鉴,以后方便考场做定海神针。

我们考虑一个多产生了\(m\)个low点的请况,记多余的low点集合为\(S_m\),那么最终在容斥多项式的第\(k\)项中\((0\leq k\leq m)\)若多出来的\(k\)个数均为集合\(S_m\)中的元素,则一定作为一个贡献出现一次,所以第\(k\)项的贡献即为\((-1)^k\cdot \dbinom{m}{k}\),所以总贡献即为:

\[\sum_{k=0}^{m}(-1)^k\cdot \dbinom{m}{k}
\]

这是一个非常常见的组合表达式,注意到,只有满足\([m=0]=1\)这样的情况下,才可以使得最终答案有贡献,总贡献为\(1\),而其余情况总贡献都因为这个等式的一个著名事实变成了\(0\)。

这其实也就意味着除了没有多选的方案,其余的都被消去了,而这就是我们希望的结果。

此外,数据范围只支持这个图上最多只有\(8\)个low点,所以容斥的复杂度是不高的。

其次是在更新图上状态时,我们用dfs回溯是最方便的选择,利用重新计算好的dp值,在dfs中更新即可。

二:code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
const int N = 12;
const int MOD = 12345678;
int n,m,lowx[N],lowy[N],cnt_low,f[2 * N + 10][(1 << N)],num,ans;
int dx[10] = {0,1,1,-1,-1,0,0,-1,1};
int dy[10] = {0,1,-1,1,-1,1,-1,0,0};
bool map[N][N],exsit[N][N]; inline bool check(int x,int y){
return x <= n && y <= m && x > 0 && y > 0;
} int calc_dp()
{
memset(f,0,sizeof(f));
cnt_low = 0;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j)
if(map[i][j])
{
lowx[++cnt_low] = i;
lowy[cnt_low] = j;
}
f[0][0] = 1;
for(int S = 0;S < (1 << cnt_low);++S)
{
int cnt = n * m;
memset(exsit,false,sizeof(exsit));
for(int i = 1;i <= cnt_low;++i)
{
if(!(S & (1 << (i - 1))))
{
if(!exsit[lowx[i]][lowy[i]])
{
exsit[lowx[i]][lowy[i]] = true;
--cnt;
for(int j = 1;j <= 8;++j)
{
int tx = lowx[i] + dx[j];
int ty = lowy[i] + dy[j];
if(check(tx,ty))
if(!exsit[tx][ty])
{
exsit[tx][ty] = true;
--cnt;
}
}
}
}
} for(int i = 0;i <= cnt;++i)
{
if(f[i][S])
{
f[i + 1][S] = (f[i + 1][S] + f[i][S] * std::max(cnt - i,0ll) % MOD) % MOD;
for(int j = 1;j <= cnt_low;++j)
{
if(!(S & (1 << (j - 1))))
f[i + 1][S | (1 << (j - 1))] = (f[i + 1][S | (1 << (j - 1))] + f[i][S]) % MOD;
}
}
}
}
return f[num][(1 << cnt_low) - 1];
} void dfs(int x,int y,int op)
{
if(x > n){
ans = (ans + calc_dp() * op + MOD) % MOD;
return ;
}
if(y > m){
dfs(x + 1,1,op);
return ;
}
dfs(x,y + 1,op);
if(!map[x][y])
{
bool flag = true;
for(int i = 1;i <= 8;++i)
{
int tx = x + dx[i];
int ty = y + dy[i];
if(check(tx,ty) && map[tx][ty]) flag = false;
}
if(flag)
{
map[x][y] = true;
dfs(x,y + 1,-op);
map[x][y] = false;//记得回溯
}
}
return ;
} signed main()
{
scanf("%d%d", &n, &m);
num = n * m;
for(int i = 1;i <= n;++i)
{
char s[11];
scanf("%s",s + 1);
for(int j = 1;j <= m;++j)
if(s[j] == 'X')
map[i][j] = true;
} for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= m;++j)
{
if(map[i][j])
for(int k = 1;k <= 8;++k)
{
if(check(i + dx[k],j + dy[k]))
if(map[i + dx[k]][j + dy[k]]) return putchar('0') && 0;
}
}
}
dfs(1,1,1);
printf("%d",ans);
return 0;
}

P3160 [CQOI2012] 局部极小值 题解的更多相关文章

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

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

  2. P3160 [CQOI2012]局部极小值

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

  3. BZOJ 2669 Luogu P3160 [CQOI2012]局部极小值 (容斥原理、DP)

    题目链接 (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2669 (luogu) https://www.luogu.org/prob ...

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

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

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

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

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

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

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

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

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

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

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

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

  10. ●BZOJ 2669 [cqoi2012]局部极小值

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2669 题解: 容斥,DP,DFS 先看看 dp 部分:首先呢,X的个数不会超过 8个.个数很 ...

随机推荐

  1. Git 中的“撤销”

    (1)当commit 完后,发现少add几个文件,可以: $ git commit -m 'initial commit' $ git add forgotten_file $ git commit ...

  2. FastAPI与MongoDB分片集群:异步数据路由与聚合优化

    title: FastAPI与MongoDB分片集群:异步数据路由与聚合优化 date: 2025/05/26 16:04:31 updated: 2025/05/26 16:04:31 author ...

  3. Ocelot和Consul 实现网关API 服务注册 负载均衡

    Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由.请求聚合.服务发现.认证.鉴权.限流熔断.并内置了负载均衡器与Service Fabric.Butterfly ...

  4. 探秘Transformer系列之(35)--- 大模型量化基础

    探秘Transformer系列之(35)--- 大模型量化基础 目录 探秘Transformer系列之(35)--- 大模型量化基础 0x00 概述 0x01 outlier 1.1 定义 1.2 特 ...

  5. Oracle DBA必备工具:自动调整重做日志文件大小和数量

    我们的文章会在微信公众号Oracle恢复实录和博客网站(www.htz.pw)同步更新 ,欢迎关注收藏.也欢迎大家转载,但请在文章开始处标注文章出处,谢谢! 由于博客中包含大量代码,建议通过网页浏览以 ...

  6. hot100之堆

    虽然更多用的是桶 数组中的第k个最大元素(215) 桶排序 class Solution { public int findKthLargest(int[] nums, int k) { int[] ...

  7. Qt+OPC开发笔记(三):OPC客户端订阅特点消息的Demo

    前言   本篇介绍opc客户端订阅消息,实现一个opc事件的订阅,当订阅的数据在服务器发生变化是,客户端能立即得到更新.   Demo      OPC客户端   OPC 客户端是一种利用OPC(OL ...

  8. HyperWorks飞机复合材料结构分析(OptiStruct)

    本练习对一个复合材料结构使用 PCOMPG 卡片进行铺层定义的过程,展示使用全局铺层编号对结果后处理的优越性.这里首先介绍了传统的定义方法即使用 PCOMP,通过对比可以显示出使用 PCOMPG 的对 ...

  9. django静态资源问题

    开发环境下 开发环境下 即settings.py中的配置项 DEBUG = True的情况. 这种情况下,django会默认帮我们处理静态文件会帮我们处理一些事情. 我们只需要少数配置即可 固定配置项 ...

  10. Rust修仙之道 第十二章:宽度境 · 类型尺寸认知与不定形之术

    第十二章:宽度境 · 类型尺寸认知与不定形之术 "形有大小,道有边界.不能测其尺者,不可轻控其灵." 当顾行云修炼 Trait 技法至高阶,试图为"无定形灵体" ...