AT_abc273_g

感觉,有时候把问题往网络流上想,不失为一种好的思路。

我们考虑 (i,j)(i,j) 上填一个 cc 的意义。则第 ii 行和第 jj 列需要填的和都会减少 cc。如果把他看作是一条 (i.j)(i.j) 之间的边,问题就变为:在两个点集之间连边,可以有重边ai,0/1a_{i,0/1} 表示第 1/21/2 个点集的第 ii 个点度数为 ai,0/1a_{i,0/1},匹配的方案数。

此时发现值域只有 33,并且知道总数和其中一个就可以推出另外两个。联想到 AT_dp_j。从前往后匹配 ai,0a_{i,0}dpi,jdp_{i,j} 表示考虑第一个点集的前 ii 个点,第二个点集中还剩 jj11 没匹配。转移是 naive 的,可以参考代码。

时空 O(n2)O(n^2),注意边界条件。

code:

int n,m,dp[N][N],a[N],b[N];
il int Mod(int x,int y){return x+y>=mod?x+y-mod:x+y;}
void Yorushika(){
	scanf("%d",&n);
	int x=0,y=0,z=0;
	rep(i,1,n)scanf("%d",&a[i]),x+=a[i];
	rep(i,1,n)scanf("%d",&b[i]),y+=b[i],z+=b[i]==1;
	if(x!=y)return puts("0"),void(0);
	dp[0][z]=1;int s=x;
	rep(i,1,n){
		if(!a[i]){
			rep(j,0,n)dp[i][j]=dp[i-1][j];
			continue;
		}
		rep(j,0,n){
			if((s-j)&1)continue;
			int k=(s-j)/2;
			if(k<0)continue;
			if(a[i]==2){
				if(j>=2)dp[i][j-2]=Mod(dp[i][j-2],1ll*dp[i-1][j]*(1ll*j*(j-1)/2%mod)%mod);
				if(k>=2)dp[i][j+2]=Mod(dp[i][j+2],1ll*dp[i-1][j]*(1ll*k*(k-1)/2%mod)%mod);
				if(k>=1)dp[i][j]=Mod(dp[i][j],1ll*dp[i-1][j]*k%mod*(j+1)%mod);
			}else{
				if(j>=1)dp[i][j-1]=Mod(dp[i][j-1],1ll*dp[i-1][j]*j%mod);
				if(k>=1)dp[i][j+1]=Mod(dp[i][j+1],1ll*dp[i-1][j]*k%mod);
			}
		}
		s-=a[i];
	}
	printf("%d\n",dp[n][0]);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}

作者以为只能是 01 矩阵,调了半个小时。什么时候才能不看错题啊。