bzoj 2669 [cqoi2012]局部极小值 DP+容斥

2669: [cqoi2012]局部极小值

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 838  Solved: 444
[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

HINT

Source

著名大佬:看到计数类问题就要想到容斥

数据范围十分小很容易想到状态压缩,而且我们发现,局部最小解最多只有8个。

我们可以用f[i][sta]表示填了1-i个数,已经填完了状态为sta的方案数,然后剩下的

随便去填,但是这样会有一个问题,就是在其它'.'的位置,如果填成了局部最小解怎么办。

这样就会有多的方案算进去。

所以就要用容斥的方法去解决这个问题。

那f[i][sta]怎么算。

格式写不出来。

cnt的话就是暴力2*8*n*m*9 一次60000的复杂度而已,9代表判断周围。

f[i][j]=f[i-1][j](前面填了1-i中就已经填好了j这个状态,那么剩下就在cntj-(i-1))选一个,(因为cnt包含了

局部最小解个数),加上,所有的当前,填这一个最小解的方案数。因为填的数是不同的,所以是不同

状态。

复杂度是 2*8 *2*8*n*m*9差不多10000000这是极限

  1 #pragma GCC optimize(2)
  2 #pragma G++ optimize(2)
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<cstdio>
  7 #include<cstring>
  8 
  9 #define MOD 12345678
 10 using namespace std;
 11 const int dx[]={-1,-1,-1,0,0,1,1,1,0};
 12 const int dy[]={-1,0,1,-1,1,-1,0,1,0};
 13 inline int read()
 14 {
 15     int x=0,f=1;char ch=getchar();
 16     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
 17     while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
 18     return x*f;
 19 }
 20 
 21 int n,m,ans;
 22 char s[10][10];
 23 
 24 int Calculate()
 25 {
 26     static pair<int,int> stack[10];
 27     static int cnt[1<<8],f[30][1<<8];
 28     int i,j,k,sta,top=0;
 29     memset(cnt,0,sizeof cnt);
 30     memset(f,0,sizeof f);
 31     for(i=1;i<=n;i++)
 32         for(j=1;j<=m;j++)
 33             if(s[i][j]=='X')
 34                 stack[++top]=pair<int,int>(i,j);
 35     for(sta=0;sta<1<<top;sta++)
 36     {
 37         static bool unfilled[10][10];
 38         memset(unfilled,0,sizeof unfilled);
 39         for(i=1;i<=top;i++)
 40             if(~sta&(1<<i-1))
 41                 unfilled[stack[i].first][stack[i].second]=true;
 42         for(i=1;i<=n;i++)
 43             for(j=1;j<=m;j++)
 44             {
 45                 for(k=0;k<9;k++)
 46                     if(unfilled[i+dx[k]][j+dy[k]])
 47                         break;
 48                 if(k==9)
 49                     cnt[sta]++;
 50             }
 51     }
 52     f[0][0]=1;
 53         for(i=1;i<=n*m;i++)
 54             for(sta=0;sta<1<<top;sta++)
 55             {
 56                 (f[i][sta]+=(long long)f[i-1][sta]*max(cnt[sta]-i+1,0))%=MOD;
 57                 for(j=1;j<=top;j++)
 58                     if(sta&(1<<j-1))
 59                         (f[i][sta]+=f[i-1][sta^(1<<j-1)])%=MOD;
 60             }
 61     return f[n*m][(1<<top)-1];
 62 }
 63 void DFS(int x,int y,int cnt)
 64 {
 65     int i;
 66     if(y==m+1)
 67     {
 68         DFS(x+1,1,cnt);
 69         return ;
 70     }
 71     if(x==n+1)
 72     {
 73         (ans+=Calculate()*cnt)%=MOD;
 74         return ;
 75     }
 76     DFS(x,y+1,cnt);
 77     for(i=0;i<9;i++)
 78         if(s[x+dx[i]][y+dy[i]]=='X')
 79             break;
 80     if(i==9)
 81     {
 82         s[x][y]='X';
 83         DFS(x,y+1,-cnt);
 84         s[x][y]='.';
 85     }
 86 }
 87 int main()
 88 {
 89     int i,j,k;
 90     n=read(),m=read();
 91     for(i=1;i<=n;i++)
 92         scanf("%s",s[i]+1);
 93     for(i=1;i<=n;i++)
 94         for(j=1;j<=m;j++)
 95             if(s[i][j]=='X')
 96                 for(k=0;k<8;k++)
 97                     if(s[i+dx[k]][j+dy[k]]=='X')
 98                         return puts("0"),0;
 99     DFS(1,1,1);
100     cout<<(ans+MOD)%MOD<<endl;
101 }

 

fi,j=fi1,jC1cntji+1+kjfi1,j{k}

posted @ 2018-03-01 09:19  Kaiser-  阅读(176)  评论(0编辑  收藏  举报