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

Posted wsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2669[cqoi2012]局部极小值 容斥+状压dp相关的知识,希望对你有一定的参考价值。

2669: [cqoi2012]局部极小值

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 774  Solved: 411
[Submit][Status][Discuss]

Description

有一个nm列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。

给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

Input

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

Output

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

Sample Input

3 2
X.
..
.X

Sample Output

60

 

容斥,推一推可以得到X的个数不超过8个(虽然我不知道是怎么推的)
枚举,从小到大填数,状压dp可以计算出对于此种图的填数方案
用cnt[s]表示状态s下可以填数的方案(包括之前已经填过的X但不包括没填的X)
f[i][s]转移就得到啦(水一波)

这样我们可以保证X的位置一定是周围最小的,但却不能保证其他位置不会出现多余的‘X‘
于是我们dfs出每一个可以为X的地方,容斥一下就好啦
 
推荐blog
http://blog.csdn.net/popoqqq/article/details/48028773
 
取模有毒
a+=b;if(a>=mod)a-=mod;
如果b是负数的话..就炸了!!
调了1h..

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define mod 12345678
#define ll long long
using namespace std;
int n,m,tp,cnt[1<<9],ok[10][10];
int dx[]={0,0,1,-1,1,-1,1,-1,0};
int dy[]={1,-1,0,0,1,-1,-1,1,0};
char mp[10][10];ll ans,f[30][1<<9];
struct node{int x,y;}p[10];
int dp(){
    memset(cnt,0,sizeof(cnt));
    memset(f,0,sizeof(f));tp=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    if(mp[i][j]==X)
    p[++tp]=(node){i,j};
    for(int st=0;st<(1<<tp);st++){
        memset(ok,0,sizeof(ok));
        for(int j=1;j<=tp;j++)
        if(!(st&(1<<(j-1))))ok[p[j].x][p[j].y]=1;
        for(int i=1;i<=n;i++)
        for(int k,j=1;j<=m;j++){
            for(k=0;k<9;k++)
            if(ok[i+dx[k]][j+dy[k]])break;
            if(k==9)cnt[st]++;
        }
    }
    f[0][0]=1;
    for(int i=1;i<=n*m;i++)
    for(int st=0;st<(1<<tp);st++){
        (f[i][st]+=f[i-1][st]*max(0,cnt[st]-i+1))%=mod;
        for(int k=1;k<=tp;k++)
        if((1<<(k-1))&st)(f[i][st]+=f[i-1][st^(1<<(k-1))])%=mod;
    }
    return f[n*m][(1<<tp)-1];
}
void dfs(int x,int y,int c){
    int t;
    if(x==n+1){
        (ans+=dp()*(c&1?-1:1))%=mod;
        return;
    }
    if(y==m)dfs(x+1,1,c);
    else dfs(x,y+1,c);
    for(t=0;t<9;t++)if(mp[dx[t]+x][dy[t]+y]==X)break;
    if(t<9)return;
    mp[x][y]=X;
    if(y==m)dfs(x+1,1,c+1);
    else dfs(x,y+1,c+1);
    mp[x][y]=.;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    if(mp[i][j]==X)
    for(int k=0;k<8;k++){
        int nx=i+dx[k],ny=j+dy[k];
        if(mp[nx][ny]==X){puts("0");return 0;}
    }
    dfs(1,1,0);
    ans<0?ans+=mod:1;
    cout<<ans;
    return 0;
}

以上是关于2669[cqoi2012]局部极小值 容斥+状压dp的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2669 CQOI2012 局部极小值 状压dp+容斥原理

●BZOJ 2669 [cqoi2012]局部极小值

bzoj2669 cqoi2012—局部极小值

[CQOI2012]局部极小值

P3160 [CQOI2012]局部极小值

NOIP2016提高A组模拟8.15Garden