[BZOJ2669][CQOI2012]局部极小值:DP+容斥原理

分析

题目要求有且只有一些位置是局部极小值。有的限制很好处理,但是只有嘛,嗯......

考虑子集反演(话说这个其实已经算是超集反演了吧还叫子集反演是不是有点不太合适),枚举题目给出位置集合的所有超集,计算让这些位置成为局部极小值,而其他位置随意的方案数,这个可以通过DP,从小到大插入每个数解决。

搜索加一些剪枝,然后就过了。。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const LL MOD=12345678;

int n,m,cnt;
int dx[8]={-1,-1,-1,0,1,1,1,0},dy[8]={-1,0,1,1,1,0,-1,-1};
int tot,X[10],Y[10],poscnt[1<<8],vis[5][10],tim;
LL f[30][1<<8],ans;
bool mp[5][10];

LL solve(){
	f[0][0]=1,tot=0;
	rin(i,1,n){
		rin(j,1,m){
			if(mp[i][j]){
				++tot;
				X[tot]=i,Y[tot]=j;
			}
		}
	}
	rin(i,0,(1<<tot)-1){
		poscnt[i]=n*m;
		++tim;
		rin(j,1,tot){
			if((i>>(j-1))&1){
				rin(k,0,7){
					int xx=X[j]+dx[k],yy=Y[j]+dy[k];
					if(xx<1||xx>n||yy<1||yy>m) continue;
					if(vis[xx][yy]!=tim){
						vis[xx][yy]=tim;
						--poscnt[i];
					}
				}
				if(vis[X[j]][Y[j]]!=tim){
					vis[X[j]][Y[j]]=tim;
					--poscnt[i];
				}
			}
		}
	}
	rin(i,1,n*m){
		rin(j,0,(1<<tot)-1){
			if(__builtin_popcount(j)>i) continue;
			f[i][j]=0;
			if(poscnt[((1<<tot)-1)^j]>=i) f[i][j]=f[i-1][j]*(poscnt[((1<<tot)-1)^j]-i+1)%MOD;
			rin(k,1,tot){
				if((j>>(k-1))&1){
					f[i][j]=(f[i][j]+f[i-1][j^(1<<(k-1))])%MOD;
				}
			}
		}
	}
	return f[n*m][(1<<tot)-1];
}

void dfs(int x,int y){
	if(x==n+1){ans=(ans+((cnt&1)==1?-1:1)*solve()%MOD+MOD)%MOD;return;}
	bool flag=true;
	rin(i,0,7){
		int xx=x+dx[i],yy=y+dy[i];
		if(xx<1||xx>n||yy<1||yy>m) continue;
		if(mp[xx][yy]){flag=false;break;}
	}
	if(y==m) dfs(x+1,1);
	else dfs(x,y+1);
	if(!flag||mp[x][y]) return;
	mp[x][y]=true;
	++cnt;
	if(y==m) dfs(x+1,1);
	else dfs(x,y+1);
	mp[x][y]=false;
	--cnt;
}

int main(){
	n=read(),m=read();
	rin(i,1,n){
		rin(j,1,m){
			char ch=getchar();
			while(ch!='X'&&ch!='.') ch=getchar();
			if(ch=='X'){
				mp[i][j]=true;
				rin(k,0,7){
					int xx=i+dx[k],yy=j+dy[k];
					if(xx<1||xx>n||yy<1||yy>m) continue;
					if(mp[xx][yy]){
						printf("0\n");
						return 0;
					}
				}
			}
		}
	}
	dfs(1,1);
	printf("%lld\n",ans);
	return 0;
}

posted on 2019-02-25 11:09  ErkkiErkko  阅读(174)  评论(0编辑  收藏  举报