●BZOJ 2669 [cqoi2012]局部极小值

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=2669

题解:

容斥,DP,DFS

先看看 dp 部分:
首先呢,X的个数不会超过 8个
个数很少,所以考虑状压,把需要填 X的那几个位置状压为二进制10表示对应的那个X位置是否已经填数。
同时填的数互不重复,考虑从小填到大。
cnt[S] 表示除了不在集合 S 里的 X 位置及其周围的位置,剩下的位置个数
定义 dp[i][S]表示从小到大填数填完了i这个数,且已经填了的 S 这个集合里的 X 位置的方案数
转移:依次去填数 1~N*M,每次有两种选择:
1).把这个数填在 某个 X 位置(枚举一个 k表示第 k个 X 位置填当前数)
dp[i][S]+=dp[i-1][S^(1<<(k-1))]

2).把这个数填在非 X 位置,那么填的位置有 cnt[S]-(i-1) 种。
dp[i][s]+=dp[i-1][s]*(cnt[s]-(i-1)) (好好理解一下这个转移)

这样 dp 可以保证那些给出的 X 位置一定是局部最小值,
因为第二种转移的填数位置都不能填在还没有填数的 X 位置的周围。
所以就完了么?
当然还没有,尽管我们保证了给出的 X 位置一定是局部最小值,
但是没有保证非 X位置一定不是非局部最小值。即,求出来的 dp[N*M][all_S(全集)]的意思是至少all_S集合里的 X位置为局部最小值的方案数
所以容斥如下:
ANS = 至少多填了0个局部最小值的方案数(dp[N*M][all_S])
          -至少多填了1个局部最小值的方案数
         +至少多填了2个局部最小值的方案数
          -....+ ....
这些用于容斥的方案数的求法:
DFS 搜索出哪些非 X 位置还可以改为 X ,
然后对于每一种新的填法,去跑一遍上述的dp即可求得对应的方案数。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define _ % mod
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
const int mv[9][2]={{0,0},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
const int mod=12345678;
char mp[10][10];
int N,M,ANS;
int solve(){
	static bool vis[10][10];
	static int dp[30][1<<8],cnt[1<<8],x[10],y[10],tot,tmp;
	tot=0; memset(dp,0,sizeof(dp));
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++) if(mp[i][j]==‘X‘)
			tot++,x[tot]=i,y[tot]=j;
	for(int s=0;s<1<<tot;s++){
		tmp=0; memset(vis,0,sizeof(vis));
		for(int i=1;i<=tot;i++) if(!(s&(1<<(i-1))))
			for(int k=0;k<9;k++)
				vis[x[i]+mv[k][0]][y[i]+mv[k][1]]=1;
		for(int i=1;i<=N;i++)
			for(int j=1;j<=M;j++)
				if(!vis[i][j]) tmp++;
		cnt[s]=tmp;
	}
	dp[0][0]=1;
	for(int i=1;i<=N*M;i++)
		for(int s=0;s<1<<tot;s++){
			dp[i][s]=(1ll*dp[i][s]+1ll*dp[i-1][s]*max(cnt[s]-(i-1),0)_)_;
			for(int k=1;k<=tot;k++) if(s&(1<<(k-1)))
				dp[i][s]=(1ll*dp[i][s]+dp[i-1][s^(1<<(k-1))])_;
		}
	return dp[N*M][(1<<tot)-1];
}
void dfs(int x,int y,int t){
	if(y==M+1){dfs(x+1,1,t);return;}
	if(x==N+1){
		int tmp=solve();
		if(t&1) tmp=(-1ll*tmp+mod)_;
		ANS=((1ll*ANS+tmp)_+mod)_;
		return;
	}
	dfs(x,y+1,t);
	bool fg=1;
	for(int k=0;k<9;k++)
		if(mp[x+mv[k][0]][y+mv[k][1]]==‘X‘) fg=0;
	if(fg){
		mp[x][y]=‘X‘;
		dfs(x,y+1,t+1);
		mp[x][y]=‘.‘;
	}
}
int main()
{
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++)
		scanf("%s",mp[i]+1);
	dfs(1,1,0);
	printf("%d",ANS);
	return 0;
}
时间: 2024-10-09 23:40:39

●BZOJ 2669 [cqoi2012]局部极小值的相关文章

BZOJ 2669 cqoi2012 局部极小值 状压DP+容斥原理

题目大意:给定一个n?m的矩阵,标记出其中的局部极小值,要求填入1...n?m,求方案数 <多年的心头大恨终于切掉了系列> 考虑将数字从小到大一个一个填进去 由于局部极小值最多8个,我们可以状压DP 令fi,j表示已经填完了前i个数,局部极小值的填充状态为j的方案数 预处理出cntj表示填充状态为j时共有多少位置是可以填充的(包括已填充的局部极小值位置) 那么有DP方程fi,j=fi?1,j?C1cntj?i+1+∑k∈jfi?1,j?{k} 但是问题是这样虽然保证了标记的位置都是局部最小值,

2669[cqoi2012]局部极小值 容斥+状压dp

2669: [cqoi2012]局部极小值 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 774  Solved: 411[Submit][Status][Discuss] Description 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值. 给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵. Input 输入第一行包含两个整数

P3160 [CQOI2012]局部极小值

题目 P3160 [CQOI2012]局部极小值 一眼就是状压,接下来就不知道了\(qwq\) 做法 我们能手玩出局部小值最多差不多是\(8,9\)个的样子,\(dp_{i,j}\)为填满\(1~i\)数字,局部小值的状态为\(j\) 第\(k\)个局部极小值填\(i\):\(dp[i][j]=(dp[i][j]+dp[i-1][j^(1<<k-1)])%p\) 不填在局部极小值,显然有些地方不能填\(i\)的,首先还没填的局部极小值不填,其周围也不能填(填\(i\)后后面再填比不符合局部极小

【bzoj2669】 cqoi2012—局部极小值

http://www.lydsy.com/JudgeOnline/problem.php?id=2669 (题目链接) 题意 给出一个$n*m$的整数矩阵,其中$[1,nm]$中的整数每个出现一次,有一些位置为局部最小值.问方案数. Solution 好神的dp啊. http://blog.csdn.net/popoqqq/article/details/48028773 $cnt_j$表示的是,在局部最小值被填充的状态为$j$的情况下,目前有多少个位置可以填,这些位置中包括已经被填了数的位置.

BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, 0) 如果x在目标状态是黑点,则连(mi(x), T, 1, 0) 设x的交换次数限制是w 如果x在两种状态中颜色相同,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0) 如果x只在初始状态为黑色,则连(in(x), mi(x), w / 2,

[CQOI2012]局部极小值

嘟嘟嘟 谁说CQOI的题都是板儿题,我就觉得这题挺难的-- 看到数据范围这么小,就会想状压.然而\(2 ^ {28}\)肯定过不了.不过对于所有的极小值的格子,最多不会超过8个,所以我们状压选了哪些局部极小值的格子(坑儿). 然后我们从小到大填数,那么对于一个数\(i\),他无非就两种填法:填入一个坑,或是填坑以外的点. 填一个坑儿就是直接填,于是有\(dp[i][S] = \sum dp[i - 1][S\) ^ \((1 << k)]\). 不填坑的话,得考虑填上这个数之后,那些还没有填的

bzoj2669 [cqoi2012]局部极小值 状压DP+容斥

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2669 题解 可以发现一个 \(4\times 7\) 的矩阵中,有局部最小值的点最多有 \(2\times 4 = 8\) 个,所以我们可以状压一下每个局部最小值的位置有没有被选. 从小到大填入每一个格子,那么如果一个点的周围有没有被填上的局部最小值,那么这个格子不可以被填.所以预处理一下每种状态下可以自由填多少格子,然后如果状态保持不变的话,就可以这样转移. 如果状态变化,就是说填了一个局

BZOJ #2669 \ CQOI 2012 局部最小值

题目描述: 在一个N * M的矩阵中填入1 ~ N * M的数,并限制一些位置为周围9个格中最小的,而其它位置不能满足这个条件. 解题思路: 考虑dp,F[i][s]表示填了前i个数,限制位置的填数状态为s,cnt[s]表示限制位置的状态为s时,可以填数的位置+限制位置已填数的数量. 那么 F[i][s] = F[i - 1][s] * (cnt[s] - i + 1) + ∑p∈s F[i - 1][s - p]. 但这样会忽略一些非限制的点满足了限制条件的情况.那我们就dfs把一些非限制点当

BZOJ 2666: [cqoi2012]组装

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2666 题意:n种零件,m个位置,每个位置有一个零件.求一个位置x,使得cost(1)+cost(2)+…+cost(n)最小.cost(i)表示 x到最近的i类型零件的距离的平方. 思路:我们最后的最优答案一定从所有m个位置中选出了n个使得每种零件恰有一个.设第i种零件的所有位置集合是Si,Si的大小是Ci 我们可以直接枚举最后选出的第i种零件是哪一个,一旦确定了这n个零件.那么