题目大意
求出满足以下条件的 n*m 的 01 矩阵个数:
(1)第 i 行第 1~li 列恰好有 1 个 1。
(2)第 i 行第 ri~m 列恰好有 1 个 1。
(3)每列至多有 1 个1。
题目分析
题意描述不清啊,这题似乎在$(l_i,r_i)$中间不能放$1$。
看了题解才会做的题。。。
首先这道题的行没什么用,我们完全可以把所有行压成一行进行考虑,统计一下在这一行哪些位置有多少个$l_i$和$r_i$。
设$f[i,j]$表示前$i$列,当前已经满足$j$个右端点的方案数。
为什么这么设状态?因为我们发现随着动规阶段向右推进,左端点是没有限制的,起到对决策限制的只有右端点。因为左端点随便往前面放能放就放总是能满足条件的,而右端点不行。
故随着阶段向右推进,若跨越了一个左端点,就从之前的空位中选择一个出来放$1$。
对于右端点的转移转移一下在当前位置放$1$还是不放。
代码
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
| #include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std; typedef long long LL; inline const LL Get_Int() { LL num=0,bj=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-')bj=-1; x=getchar(); } while(x>='0'&&x<='9') { num=num*10+x-'0'; x=getchar(); } return num*bj; } const LL mod=998244353; int n,m,l[3005],r[3005],sl[3005],sr[3005]; LL f[3005][3005]; int main() { n=Get_Int(); m=Get_Int(); for(int i=1; i<=n; i++) { l[i]=Get_Int(); r[i]=Get_Int(); sl[l[i]]++; sr[r[i]]++; } for(int i=1; i<=m; i++) { sl[i]+=sl[i-1]; sr[i]+=sr[i-1]; } f[0][0]=1; for(int i=1; i<=m; i++) { f[i][0]=f[i-1][0]; for(int j=1; j<=i; j++)f[i][j]=(f[i-1][j]+f[i-1][j-1]*(sr[i]-(j-1)))%mod; for(int j=sl[i-1]; j<sl[i]; j++) for(int k=0; k<=i; k++) f[i][k]=f[i][k]*(i-k-j)%mod; } printf("%lld\n",f[m][n]); return 0; }
|