题目链接:http://uoj.ac/problem/422。
考虑$\rm min-max$容斥(我随机选的题为啥全是min-max容斥。。),那么就有一个很简单的暴力:枚举集合,假设有$x$种方案会碰到一个黑点,那么贡献就是$(-1)^{|s|}\cdot \dfrac{2nm-n-m}{x}$,分子是总方案数。
我们发现需要知道的东西只有集合大小和$x$,并且$x$是$O(nm)$级别的,很容易得到一个$\rm dp$:$f_{i,s,k}$表示考虑了前$i$列,最后一列选择的状态是$s$,$x=k$的方案数。注意到我们没有必要把大小开在状态里,只需要每次转移的时候乘上一个$(-1)^{|s|}$的因子就好了。
因为要枚举状态然后$O(2^n)$转移,复杂度就是$O(2^{2n}nm^2)$,这个东西稍微大了点过不了。。我卡了好久的常没卡过。。害得我重构代码
考虑优化,可以利用轮廓线$\rm dp$,因为当前点增加的贡献只取决于他上面那个点和左边那个点,那么加一维状态$x$表示当前处理完$(x,i)$这个点,并且$s$前$i$位是第$i$行的状态,后面是$i-1$的状态,转移就变成了$O(1)$了。
复杂度$O(2^nn^2m^2)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++) #define cnt __builtin_popcount
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 998244353;
int n,m,f[2][7][66][1222]; char ss[7][102];
int qpow(int a,int x) { int res=1; for(;x;x>>=1,a=1ll*a*a%mod) if(x&1) res=1ll*res*a%mod; return res; }
void add(int &x,int y) {x+=y;if(x>=mod) x-=mod;}
int main() { read(n),read(m); for(int i=1;i<=n;i++) scanf("%s",ss[i]+1); f[0][n][0][0]=mod-1;int all=(1<<n)-1; for(int i=1,p=1;i<=m;i++,p^=1) { memset(f[p],0,sizeof f[0]); memcpy(f[p][0],f[p^1][n],sizeof f[0][0]); for(int j=1;j<=n;j++) { auto make=[&] (int s) { int x=0; if(j>1&&(s&(1<<(j-2)))) x++; if(i>1&&(s&(1<<(j-1)))) x++; for(int k=0;k<=2*i*n-i-n;k++) add(f[p][j][s&(all-(1<<(j-1)))][k+x],f[p][j-1][s][k]); }; if(ss[j][i]=='.') { for(int s=0;s<1<<n;s++) make(s); } else { for(int s=0;s<1<<n;s++) { make(s); int x=(j>1)+(i>1); for(int k=0;k<=2*i*n-i-n;k++) add(f[p][j][s|(1<<(j-1))][k+x],(mod-f[p][j-1][s][k])%mod); } } } } int ans=0; for(int s=0;s<1<<n;s++) for(int k=0;k<=2*n*m-n-m;k++) ans=(ans+1ll*f[m&1][n][s][k]*qpow(k,mod-2)%mod)%mod; ans=1ll*ans*(2*n*m-n-m)%mod; write((ans+mod)%mod); return 0; }
|