求出满足以下条件的 n*m 的 01 矩阵个数:
(1)第 i 行第 1~li 列恰好有 1 个 1。
(2)第 i 行第 ri~m 列恰好有 1 个 1。
(3)每列至多有 1 个 1
这题还是很有趣的,模拟一下样例就想出dp方法了
最难想到的是要按列来枚举……
详细见代码注释吧
代码:
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 3005
#define pos1 (rsum[i]-j+1)
#define pos2 (i-j-k)
using namespace std;
int n,m;
ll f[N][N],delta1,delta2;//f[i][j]表示前i列中有j列的右区间放了1
ll lsum[N],rsum[N];//1~l和r~m已经放了多少个1
struct Limit
{
ll l,r;
}a[N];
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
ll Mod(ll x){return x%mod;}
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
read(n);read(m);
for(register int i=1;i<=n;++i)
{
read(a[i].l);
read(a[i].r);
lsum[a[i].l]++;
rsum[a[i].r]++;
}
f[0][0]=1;
for(register int i=1;i<=m;++i)//枚举前 i 列
{
f[i][0]=f[i-1][0];
lsum[i]=lsum[i]+lsum[i-1];
rsum[i]=rsum[i]+rsum[i-1];
for(register int j=1;j<=i;++j)
{
delta1=Mod(f[i-1][j]);//右边不放1,直接转移
delta2=Mod(f[i-1][j-1]*pos1);//右边放1,方案数乘上剩余的位置
f[i][j]=Mod(delta1+delta2);
}
for(register int j=lsum[i-1];j<=lsum[i]-1;++j)//枚举左边还差多少个1
{
for(register int k=0;k<=i;++k)//共有k个右区间放了1
f[i][k]=Mod(f[i][k]*pos2);//贡献=方案数*剩余位置
}
}
printf("%lld\n",f[m][n]);
return 0;
}
/*
5 200
60 170
50 120
80 90
70 110
80 100
*/