BZOJ 2669 Luogu P3160 [CQOI2012]局部极小值 (容斥原理、DP)
题目链接
(bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2669
(luogu) https://www.luogu.org/problem/P3160
题解
这道题充分暴露了我的菜。。
显然两个局部极小值点不能相邻,所以最多有\(8\)个局部极小值。
然后考虑容斥掉.
不能成为局部极小值的限制,那么就变成钦定某些位置一定是局部极小值,其余不限,求方案数。
然后这个可以状压DP,考虑从小到大加入每个数,然后就很好求了。
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iostream>
#include<vector>
using namespace std;
const int P = 12345678;
const int dx[8] = {1,0,-1,0,1,1,-1,-1},dy[8] = {0,1,0,-1,1,-1,-1,1};
vector<int> kx,ky;
int bitcnt[(1<<8)+3];
char a[7][11];
char b[7][11];
int num[(1<<8)+3];
int dp[31][(1<<8)+3];
int n,m,cnt,ans;
bool check(int x,int y,int typ)
{
bool ret = true;
for(int i=0; i<8; i++)
{
int xx = x+dx[i],yy = y+dy[i];
if(xx>0&&xx<=n&&yy>0&&yy<=m)
{
if(typ==0) {if(a[xx][yy]=='X') {return false;}}
if(typ==1) {if(b[xx][yy]=='X') {return false;}}
}
}
return true;
}
int calc()
{
kx.clear(); ky.clear();
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(a[i][j]=='X') {kx.push_back(i); ky.push_back(j);}
}
}
for(int i=0; i<(1<<kx.size()); i++)
{
for(int j=0; j<kx.size(); j++)
{
if(!(i&(1<<j))) {b[kx[j]][ky[j]] = 'X';}
}
num[i] = 0;
for(int j=1; j<=n; j++)
{
for(int k=1; k<=m; k++)
{
bool ok = check(j,k,1);
if((ok && a[j][k]!='X')) num[i]++;
}
}
num[i] += bitcnt[i];
for(int j=0; j<kx.size(); j++) {b[kx[j]][ky[j]] = '.';}
}
dp[0][0] = 1;
for(int i=0; i<n*m; i++)
{
for(int j=0; j<(1<<kx.size()); j++)
{
if(dp[i][j])
{
dp[i+1][j] = (dp[i+1][j]+dp[i][j]*(num[j]-i))%P;
for(int k=0; k<kx.size(); k++)
{
if(!(j&(1<<k)))
{
dp[i+1][j|(1<<k)] = (dp[i+1][j|(1<<k)]+dp[i][j])%P;
}
}
}
}
}
int ret = dp[n*m][(1<<kx.size())-1];
for(int i=0; i<=n*m; i++) for(int j=0; j<(1<<kx.size()); j++) dp[i][j] = 0;
return ret;
}
void dfs(int x,int y,int dep)
{
if(x==n+1)
{
int tmp = calc();
if((dep-cnt)&1) {ans = ans-tmp<0 ? ans-tmp+P : ans-tmp;}
else {ans = ans+tmp>=P ? ans+tmp-P : ans+tmp;}
return;
}
int xx = x,yy = y+1; if(yy>m) {yy = 1; xx++;}
if(a[x][y]=='X')
{
bool f = check(x,y,0);
if(f) {dfs(xx,yy,dep+1);}
}
else
{
a[x][y] = 'X';
bool f = check(x,y,0);
if(f) {dfs(xx,yy,dep+1);}
a[x][y] = '.';
dfs(xx,yy,dep);
}
}
int main()
{
for(int i=1; i<(1<<8); i++) bitcnt[i] = bitcnt[i>>1]+(i&1);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) b[i][j] = '.';
for(int i=1; i<=n; i++) scanf("%s",a[i]+1);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++) {if(a[i][j]=='X') cnt++;}
}
if(cnt>8) {printf("0"); return 0;}
dfs(1,1,0);
printf("%d\n",ans);
return 0;
}
BZOJ 2669 Luogu P3160 [CQOI2012]局部极小值 (容斥原理、DP)的更多相关文章
- P3160 [CQOI2012]局部极小值 题解(状压DP+容斥)
题目链接 P3160 [CQOI2012]局部极小值 双倍经验,双倍快乐 解题思路 存下来每个坑(极小值点)的位置,以这个序号进行状态压缩. 显然,\(4*7\)的数据范围让极小值点在8个以内(以下示 ...
- P3160 [CQOI2012]局部极小值
题目 P3160 [CQOI2012]局部极小值 一眼就是状压,接下来就不知道了\(qwq\) 做法 我们能手玩出局部小值最多差不多是\(8,9\)个的样子,\(dp_{i,j}\)为填满\(1~i\ ...
- 【bzoj2669】[cqoi2012]局部极小值 容斥原理+状压dp
题目描述 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值. 给出所有局部极小值的位置,你的任 ...
- [BZOJ2669][CQOI2012]局部极小值:DP+容斥原理
分析 题目要求有且只有一些位置是局部极小值.有的限制很好处理,但是只有嘛,嗯...... 考虑子集反演(话说这个其实已经算是超集反演了吧还叫子集反演是不是有点不太合适),枚举题目给出位置集合的所有超集 ...
- BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)
题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042 (Luogu) https://www.luogu.org/prob ...
- BZOJ 4417 Luogu P3990 [SHOI2013]超级跳马 (DP、矩阵乘法)
题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=4417 (luogu)https://www.luogu.org/prob ...
- BZOJ 3143 Luogu P3232 [HNOI2013]游走 (DP、高斯消元)
题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=3143 (luogu) https://www.luogu.org/pro ...
- bzoj 1042: [HAOI2008]硬币购物【容斥原理+dp】
当然是容斥啦. 用dp预处理出\( f[i] \),表示在\( i \)价格时不考虑限制的方案数,转移方程是\( f[i]+=f[i-c[j]] \),用状压枚举不满足的状态容斥一下即可. #incl ...
- BZOJ 3162 / Luogu P4895: 独钓寒江雪 树hash+DP
题意 给出一棵无根树,求本质不同的独立集数模100000000710000000071000000007的值. n≤500000n\le 500000n≤500000 题解 如果是有根树就好做多了.然 ...
随机推荐
- Java设计模式之外观模式和最少知识原则
外观模式: 外观模式:提供一个统一的接口,来访问子系统中一群功能相关接口(类似一键启动,一键关闭等等) 外观模式定义了一个高层接口,让子系统更容易使用 降低对外接口耦合度 外观模式和命令模式各自侧重点 ...
- 测试必知150个常用Linux命令,已为各位筛选整理
●线上查询及帮助命令(1 个) help 如:mkdir --help ●文件和目录操作命令(12 个) ls tree pwd mkdir rmdir cd touch cp mv r ...
- java 如何编写多线程的代码
线程是干活的所以线程一定是Thread,或者改线程实现Runnable接口多线程是竞争关系,所以多个线程竞争同一个资源,也就是同一个对象所以这个竞争对象发到Thread中即: // resources ...
- 帝国cms 重置用户名和密码
5.1至7.0版本:用phpmyadmin修改phome_enewsuser表里的记录:把password字段的值设为:“322d3fef02fc39251436cb4522d29a71”:把salt ...
- node + express搭建api项目
express框架 描述 express是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,为 Web 和移动应用程序提供一组强大的功能. 安装 // 1.使用npm淘宝镜像--cn ...
- 9、linux权限-ACL权限
来自为知笔记(Wiz)
- 10、LNMP架构
1LNMP架构概述 1.1.什么是LNMP LNMP 是一套技术的组合,L = Linux,N = Nginx,M~ = MySQL,P~ = PHP 1.2.LNMP架构是如何工作的 首先Ngin ...
- linux svn 安装
1.环境centos6.4 2.安装svnyum -y install subversion 3.配置 建立版本库目录mkdir /var/www/svndata svnserve -d -r /va ...
- parfile解决exp时tables过多问题
parfile 一般用于表数据过大.使用导出.导入命令参数过多等场景: 在对oracle数据库使用exp命令导出数据时,如果tables=后面跟的表比较多,就是导致命令行放不下,从而不能导出.百度一把 ...
- 快读代码level.2
long long read() { long long ans=0; char last=' ',ch=getchar();//last用来存正负号,并消去那些换行符,空格 ') { last=ch ...