题目传送门
好题呀
约定:为方便描述,将 “和为 2 2 2 的行”简称为 2 2 2 类行,和为 1 1 1 的简称为 1 1 1 类行,列同理。
题目中虽然指定了每行和每列的和 ,但其实我们只要求出 有对应 行数和列数的方案数即可
f i , j , k : 填了前 i 行,有 j 列为 1 类列, k 列为 2 类列 f_{i,j,k} : 填了前i行,有j列为1类列,k列为2类列 fi,j,k:填了前i行,有j列为1类列,k列为2类列
考虑到前 i i i 行一共要放 ∑ j = 1 i R i \sum_{j=1}^i R_i ∑j=1iRi,记 s u m i = ∑ j = 1 i R i sum_i=\sum_{j=1}^i R_i sumi=∑j=1iRi,那么明显若已知 j j j , 则 k = s u m i − j 2 k=\frac{sum_i-j}{2} k=2sumi−j
就可以简化状态:
f i , j : 填了前 i 行,有 j 列为 1 类列, s u m i − j 2 列为 2 类列 f_{i,j} : 填了前i行,有j列为1类列,\frac{sum_i-j}{2}列为2类列 fi,j:填了前i行,有j列为1类列,2sumi−j列为2类列
用三维的状态比较好理解转移,改成二维只需去掉 k k k 即可
根据下一列的 R i R_i Ri 转移
f i , j , k → f i + 1 , j , k ( R i + 1 = 0 ) f i , j , k ∗ ( n − j − k ) → f i + 1 , j + 1 , k , f i , j , k ∗ j → f i + 1 , j − 1 , k + 1 ( R i + 1 = 1 ) f i , j , k ∗ C 2 n − j − k → f i + 1 , j + 2 , k , f i , j , k ∗ C 2 j → f i + 1 , j − 2 , k + 2 , f i , j , k ∗ ( n − j − k ) ∗ ( j + 1 ) → f i , j , k + 1 ( R i + 1 = 2 ) \begin{aligned} &f_{i,j,k}\to f_{i+1,j,k} \quad (R_{i+1}=0)\\ &f_{i,j,k}*(n-j-k) \to f_{i+1,j+1,k},f_{i,j,k}*j\to f_{i+1,j-1,k+1} \quad (R_{i+1}=1)\\ &f_{i,j,k}*C_{2}^{n-j-k}\to f_{i+1,j+2,k},f_{i,j,k}*C_{2}^{j}\to f_{i+1,j-2,k+2},f_{i,j,k}*(n-j-k)*(j+1)\to f_{i,j,k+1}\quad(R_{i+1}=2)\\ \end{aligned} fi,j,k→fi+1,j,k(Ri+1=0)fi,j,k∗(n−j−k)→fi+1,j+1,k,fi,j,k∗j→fi+1,j−1,k+1(Ri+1=1)fi,j,k∗C2n−j−k→fi+1,j+2,k,fi,j,k∗C2j→fi+1,j−2,k+2,fi,j,k∗(n−j−k)∗(j+1)→fi,j,k+1(Ri+1=2)
注意限制条件,比如 f i , j , k ∗ ( n − j − k ) ∗ ( j + 1 ) → f i , j , k + 1 f_{i,j,k}*(n-j-k)*(j+1)\to f_{i,j,k+1} fi,j,k∗(n−j−k)∗(j+1)→fi,j,k+1 这个转移必须要 j + k < n j+k
最后,想想设的状态没有规定每列的和是题目指定的值,只是数量满足了,所以最后要除去排列数
注意代码实现要按之前说的省去第三维 k k k
#include
#include
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define _rep(i,j,k) for(int i=k;i>=j;i--)
#define k ((sum[i]-j)/2)
using ll = long long ;
using db = double ;
using namespace std;
const int N=5e3+7;
const ll mod=998244353,inv2=499122177;
int n,c1;
int R[N],C[N];
ll f[N][N],sum[N],fac[N],ifac[N],ss;
ll qpow(ll ba,ll pow) {
ll res=1; while(pow) {
if(pow&1) res=res*ba%mod;
ba=ba*ba%mod,pow>>=1;
} return res;
}
ll Add(ll x,ll y) { return ((x+y)%mod) ;}
ll Mul(ll x,ll y) { return ((x*y)%mod) ;}
ll Inv(ll x) { return qpow(x,mod-2) ;}
ll c(ll x,ll y) { return fac[x]*ifac[y]%mod*ifac[x-y]%mod; }
void init() {
scanf("%d",&n);
rep(i,1,n) scanf("%d",&R[i]),sum[i]=sum[i-1]+R[i];
rep(j,1,n) scanf("%d",&C[j]),c1+=(C[j]==1),ss+=C[j];
fac[0]=1; rep(i,1,n) fac[i]=fac[i-1]*i%mod;
ifac[n]=Inv(fac[n]); _rep(i,1,n) ifac[i-1]=ifac[i]*i%mod;
}
void work() {
if(sum[n]!=ss) {puts("0");return ;}
f[0][0]=1;
rep(i,0,n) rep(j,0,n) if(sum[i]>=j && j+k<=n){
if(R[i+1]==0) f[i+1][j]=Add(f[i+1][j],f[i][j]);
else if(R[i+1]==1) {
if(k+j1) f[i+1][j-2]=Add(f[i+1][j-2],Mul(f[i][j],j*(j-1)%mod*inv2%mod));
if(k+j