• BZOJ 2669 【CQOI2012】 局部极小值


    题目链接:局部极小值

      这是一道(dp)好题。

      由于需要保证某些位置比周围都要小,那么我们可以从小到大把每个数依次填入,保证每个局部极小值填入之前周围都不能填,就只需要在加入的时候计数了。

      由于局部极小值最多只可能出现(8)个,所以我们可以直接状压当前所有局部极小值位置的状态。(f_{i,S})表示当前填完了(i)个数,局部极小值状态为(S)的方案数。同时我们还需要一个(cnt)数组,(cnt_S)表示当局部极小值状态为(S)时有多少个位置不能填。

      这样转移方程就很显然了。可以从(f_{i,S})转到(f_{i+1,S}),方案数为(nm-i-cnt[S]);或者直接在某个局部极小值位置上填一个数。

      但是这样会有问题,可能会导致某些非局部极小值的位置成为局部极小值。于是我们就可以枚举每个非局部极小值的位置是否是局部极小值,然后容斥掉即可。这样枚举的方案数看似很多,实际上最坏是不到(2 imes 10^4)的。注意需要处理矩阵不合法的情况。

      下面贴代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    #define mod 12345678
    
    using namespace std;
    typedef long long llg;
    
    int zx[8]={-1,0,1,1,1,0,-1,-1};
    int zy[8]={1,1,1,0,-1,-1,-1,0};
    int n,m,cnt[1<<8],tol,fr[1<<8];
    int f[29][1<<8],op[1<<8],ans;
    int ci[5][8],ax[8],ay[8],la;
    char s[5][8];
    
    void work(int x,int y,int S,int z){
    	for(int k=0,i,j;k<8;k++){
    		i=x+zx[k],j=y+zy[k];
    		if(i>0 && i<=n && j>0 && j<=m){
    			if(!ci[i][j]) cnt[S]++;
    			ci[i][j]+=z;
    		}
    	}
    }
    
    void dp(){
    	int now=1<<la;
    	for(int S=0;S<now;S++){
    		cnt[S]=fr[S];
    		for(int x=S,j;j=x&(-x),x;x-=j)
    			work(ax[op[j]+1],ay[op[j]+1],S,1);
    		for(int x=S,j;j=x&(-x),x;x-=j)
    			work(ax[op[j]+1],ay[op[j]+1],S,-1);
    	}
    	for(int i=0;i<=n*m;i++)
    		for(int S=0;S<now;S++) f[i][S]=0;
    	f[0][now-1]=1;
    	for(int i=0;i<n*m;i++){
    		for(int S=0,x;S<now;S++){
    			(f[i+1][S]+=f[i][S]*(n*m-i-cnt[S]))%=mod;
    			for(x=S;x;x-=x&(-x))
    				(f[i+1][S^(x&(-x))]+=f[i][S])%=mod;
    		}
    	}
    	if(tol) (ans-=f[n*m][0])%=mod;
    	else (ans+=f[n*m][0])%=mod;
    }
    
    void print(){printf("0");exit(0);}
    void dfs(int x,int y){
    	if(y==m+1) x++,y=1;
    	if(x==n+1){dp();return;}
    	bool ww=s[x][y]=='.';
    	for(int k=0;k<8;k++)
    		if(s[x+zx[k]][y+zy[k]]=='X') ww=0;
    	if(ww) s[x][y]='X',tol++;
    	if(s[x][y]=='X'){
    		la++,ax[la]=x,ay[la]=y;
    		dfs(x,y+1),la--;
    	}
    	if(ww) s[x][y]='.',tol--;
    	if(s[x][y]=='.') dfs(x,y+1);
    }
    
    int main(){
    	File("a");
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) if(s[i][j]=='X')
    			for(int k=0;k<8;k++)
    				if(s[i+zx[k]][j+zy[k]]=='X') print();
    	for(int i=0;i<(1<<8);i++) fr[i]=fr[i>>1]+(i&1);
    	for(int i=0;i<8;i++) op[1<<i]=i;  dfs(1,1);
    	printf("%d",(ans+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    .NET笔试题集(四)
    jquery弹出遮掩层
    LINQ学习(八):强制立即执行
    .NET笔试题集(五)
    LINQ学习(六):OrderBy/Group By子句
    .NET笔试题集(二)
    LINQ学习(九):LINQ to SQL简单应用
    TFS Branch脚本
    分享幻灯片+在线阅读
    C#笔记(五):文件和流
  • 原文地址:https://www.cnblogs.com/lcf-2000/p/6650079.html
Copyright © 2020-2023  润新知