Description
一个n*m的01矩阵,每一行给出li和ri
现在给出限制条件,对于每一行
第 1~li列恰好有 1个 1,第 ri~m列恰好有 1个 1。
对于每一列,至多有 1个 1。
【解题报告】
都写在代码里了
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N=3010;
const int maxn=3000;
const int mod=998244353;
int c[N][N],dp[N][N];
int cj[N];
int sl[N],sr[N],s[N];
int cl[N],cr[N];
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
int l,r;
int n,m;
for(int i=0;i<N;++i)
{
c[i][0]=1;
for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
cj[0]=1;for(int i=1;i<N;++i) cj[i]=1ll*cj[i-1]*i%mod;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&l,&r);
cl[l]++;
cr[r]++;
}
for(int i=1;i<=m;++i)
{
sl[i]=sl[i-1]+cl[i];
sr[i]=sr[i-1]+cr[i];
s[i]=sl[i]+sr[i];
}
dp[0][0]=1;
for(int i=1;i<=m;++i)
{
int k,c1,c2;
for(int j=0;j<=sr[i-1];++j)
{
//用dp[i][j]表示做到前i列,对于已经跨过的左区间我们已经填了1了,对于碰到的右区间我们已经填了一部分1,但还有j和右区间待定
if(!dp[i-1][j]) continue;
k=i-1-s[i-1]+j;//当前列左边留下的可以填的空
if(k<0) continue;
//在更新到当前列时我们会碰到一些右区间,我们可以选择一个区间填上1,也可以把他们全部待定
c1=cl[i]<=k?1ll*c[k][cl[i]]*cj[cl[i]]%mod:0;
//如果我们填上一个1的之前的待定填法
c2=cl[i]<=k+1?1ll*c[k+1][cl[i]]*cj[cl[i]]%mod:0;
//如果我们全部待定,则对于之前的待定区间,有k+1个空,填上cl(i)个1的方案
dp[i][j+cr[i]]=(dp[i][j+cr[i]]+1ll*dp[i-1][j]*c2)%mod;
//多有新加入的右区间全部待定
if(j+cr[i]) dp[i][j+cr[i]-1]=(dp[i][j+cr[i]-1]+1ll*dp[i-1][j]*c1%mod*(j+cr[i]))%mod;
//填上一个右区间
}
}
printf("%d",dp[m][0])%mod;
}