zoukankan      html  css  js  c++  java
  • [CQOI2012]局部极小值

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

    Input
    输入第一行包含两个整数n和m(1<=n<=4, 1<=m<=7),即行数和列数。
    以下n行每行m个字符,其中“X”表示局部极小值,“.”表示非局部极小值。

    Output
    输出仅一行,为可能的矩阵总数除以12345678的余数。

    Sample Input
    3 2
    X.
    ..
    .X

    Sample Output
    60

    --

    我们考虑把(1~n imes m)依次往矩阵里填

    因为局部最小值最多就只有8个,所以我们可以把他们压缩起来

    (f[sta][i])表示填到第i个数,局部最小值的填写状态为sta的方案数

    那么第i个数有两种填写方法,'X'位置和'.'位置

    如果填在'X'位置,我们直接填就好,因为我们是按从小到大来填的,那么有(f[sta|(1<<(k-1))][i+1]+=f[sta|(1<<(k-1))][i])

    如果填在'.'位置,那么显然有些位置是不能填的,就是那些'X'周围的位置,那么我们可以预处理出每个状态(sta)中可以随便填的位置个数(p[sta]),转移为(f[sta][i+1]+=f[sta][i] imes max(p[sta]-i+1,0))

    然后这样算出来也会有问题,因为我们只保证了'X'为局部最小值,没有保证'.'不为局部最小值,那么我们容斥一下即可

    (ps:(p[])可以不用预处理,在代码里有注释)

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')    f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x>=10)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=10,p=12345678;
    const int dx[9]={-1,-1,-1,0,0,1,1,1,0};
    const int dy[9]={-1,0,1,-1,1,-1,0,1,0};
    char map[N][N];
    int tx[N],ty[N],f[(1<<N)+10][N*N],A[N][N];
    int n,m,Ans;
    bool in_map(int x,int y){return x>0&&x<=n&&y>0&&y<=m;}
    int get(){
    	int tot=0;
    	for (int x=1;x<=n;x++)	for (int y=1;y<=m;y++)	if (map[x][y]=='X')	tx[tot]=x,ty[tot]=y,tot++;
    	memset(f,0,sizeof(f));
    	memset(A,255,sizeof(A));
    	f[0][0]=1;
    	for (int sta=0;sta<1<<tot;sta++){
    		for (int i=0;i<tot;i++)
    			if (!(sta&(1<<i)))
    				for (int k=0;k<9;k++)
    					if (in_map(tx[i]+dx[k],ty[i]+dy[k]))
    						A[tx[i]+dx[k]][ty[i]+dy[k]]=sta;
    		int cnt=0;//p[]没必要预处理,开个数组在当前状态存下来即可,因为我是用当前状态更新之后的状态
    		for (int x=1;x<=n;x++)	for (int y=1;y<=m;y++)	if (A[x][y]!=sta)	cnt++;
    		for (int i=0;i<=n*m;i++){
    			if (f[sta][i]){
    				f[sta][i+1]=(f[sta][i+1]+f[sta][i]*max(cnt-i,0))%p;
    				for (int j=0;j<tot;j++)
    					if (!(sta&(1<<j)))
    						f[sta|(1<<j)][i+1]=(f[sta|(1<<j)][i+1]+f[sta][i])%p;
    			}
    		}
    	}
    	return f[(1<<tot)-1][n*m];
    }
    void search(int x,int y,int k){//容斥搜索
    	if (x>n){Ans=(Ans+k*get())%p;return;}
    	if (y>m){search(x+1,1,k);return;}
    	search(x,y+1,k);
    	for (int k=0;k<9;k++)	if (in_map(x+dx[k],y+dy[k])&&map[x+dx[k]][y+dy[k]]=='X')	return;
    	map[x][y]='X',search(x,y+1,-k),map[x][y]='.';//容斥
    }
    int solve(){
    	//首先判非法状态
    	for (int x=1;x<=n;x++)
    		for (int y=1;y<=m;y++)
    			if (map[x][y]=='X')
    				for (int k=0;k<8;k++)
    					if (in_map(x+dx[k],y+dy[k])&&map[x+dx[k]][y+dy[k]]=='X')
    						return 0;
    	Ans=0;
    	search(1,1,1);
    	Ans=(Ans+p)%p;
    	return Ans;
    }
    int main(){
    	n=read(),m=read();
    	for (int i=1;i<=n;i++)	scanf("%s",map[i]+1);
    	printf("%d
    ",solve());
    	return 0;
    }
    
  • 相关阅读:
    PKU 学生的反馈 20091
    PKU 学生的反馈 2009 –2
    中国队有进步
    刚发现博客园又遇到了问题
    今日计划
    Delphi中使用多线程
    在老ASP中使用对象的对象生存期问题
    ASP与Javascript
    ASP & VBScript的错误处理
    对前一段时间学习网络和多线程编程的总结
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/9692854.html
Copyright © 2011-2022 走看看