matrix
这个题是一道dp
虽然是一个二维的矩阵,但是状态转移方程表示的却是有一维
f[i][j] 表示到第i列,有j行的右区间中有一个1
考虑如何转移,枚举每一列,因为j表示的是有区间有多少已经有1,那么左区间是不用一开始就考虑的,因为只考虑左区间的话,那么肯定是先往小的里放,所以我们可以在每一次扫到一个左区间的右端点后,在统计右端点以内有多少个空着的列,然后放入这些区间,
对于怎么统计这些空区间,可以用两个前缀和lsum[],rsum[],分别记录到i为止,有几个左区间的右端点和右区间的左端点,那么当枚举到i的时候,第i列显然有lsum[i]-lsum[i-1]个右端点 ,但是占据1~i这些列的还有j个右区间,所以可选的空位置有(i-lsum[i-1]-j)个,那么对答案的贡献就是A(i-lsum[i-1]-j,lsum[i]-lsum[i-1]);
然后考虑有区间的转移,当从i转移到i+1时,那么一定有rsum[i+1]个右区间可以在i+1的位置上放上1,但是之前可能一应放过了,所以在第i+1列放1的选择为srum[i+1]-j;
这样转移下去就行了
CODE:
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <iostream> 6 #include <algorithm> 7 # define maxn 3010 8 # define mod 998244353 9 using namespace std; 10 typedef long long LL; 11 int n,m; 12 int L[maxn],R[maxn]; 13 int cun[maxn]; 14 LL sl[maxn],sr[maxn]; 15 LL f[maxn][maxn]; 16 LL jc[maxn],njc[maxn]; 17 LL ksm(LL a,LL b){ 18 LL ans=1; 19 while(b){ 20 if(b&1) ans*=a,ans%=mod; 21 b=b>>1; a*=a; a%=mod; 22 } 23 return ans; 24 } 25 void beg(){ 26 memset(cun,0,sizeof(cun)); 27 for(int i=1;i<=n;i++) cun[L[i]]++; 28 for(int i=1;i<=m;i++) sl[i]=sl[i-1]+cun[i]; 29 memset(cun,0,sizeof(cun)); 30 for(int i=1;i<=n;i++) cun[R[i]]++; 31 for(int i=1;i<=m;i++) sr[i]=sr[i-1]+cun[i]; 32 jc[0]=1; njc[0]=1; 33 for(int i=1;i<=max(n,m);i++) jc[i]=jc[i-1]*i,jc[i]%=mod; 34 for(int i=1;i<=max(n,m);i++) njc[i]=ksm(jc[i],mod-2); 35 } 36 LL A(int n,int m){ 37 if(n<0 || m<0) return 0; 38 if(m>n) return 0; 39 return jc[n]*njc[n-m]%mod; 40 } 41 void DP(){ 42 f[1][0]=1; 43 for(int i=1;i<m;i++){ 44 for(int j=0;j<=n;j++){ 45 f[i+1][j]+=f[i][j]; 46 if(sr[i+1]-j>0){ 47 f[i+1][j+1]+=f[i][j]*(sr[i+1]-j); 48 f[i+1][j+1]%=mod; 49 } 50 } 51 for(int j=0;j<=n;j++){ 52 f[i+1][j]=f[i+1][j]*A(i+1-sl[i]-j,sl[i+1]-sl[i]); 53 f[i+1][j]%=mod; 54 } 55 } 56 cout<<f[m][n]<<endl; 57 } 58 int main(){ 59 // freopen("b.in","r",stdin); 60 scanf("%d%d",&n,&m); 61 for(int i=1;i<=n;i++) scanf("%d%d",&L[i],&R[i]); 62 beg(); 63 DP(); 64 }