matrix(又是dp....)
f[i][j] : 处理到第i列,填了j行右区间(这些右区间的左端点都在i列或i列左边)。
you[i]:有多少个右区间的左端点都在i列或i列左边。
zuo[i]:有多少个左区间的右端点都在i列或i列左边。
如何转移?
先只考虑填右区间的情况。
f[i][j] = f[i-1][j] + f[i] + f[i-1][j-1] * (i - j + 1)。
i-1列是已经填了j个。
和i列时填第j个,第j个不一定必须在i列上,i列及i列以前的空位。
再考虑左区间。
根据乘法原理 : 总情况数 = 左区间情况数 × 右区间情况数。
有zuo[i] - zuo[i-1]行的左区间的右端点在第i列。
但他们不一定必须填在第i列,可以填在前面的空格。
所以我们对于每个f[i][j];我们一个一个的去填zuo[i-1] <= k <zuo[i]
k表示左区间已填多少个,填第k+1个左区间时,左区间已经填k个。
其实相当于在i-j个空位置中选 zuo[i] - zuo[i-1]个位置填左区间有多少种方法。
f[i][j] = f[i][j] * (i -k - j);
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int n,m, f[3005][3005], zuo[3005], you[3005];
void read(int &x)
{
x = 0;
int f = 0;
char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = 1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = 10 * x + c - '0';
c = getchar();
}
if(f) x = -x;
}
int main()
{
read(n);
read(m);
for(int i = 1; i <= n; i++)
{
int l,r;
read(l);read(r);
zuo[l] ++;
you[r] ++;
}
for(int i = 2; i <= m; i++)
{
zuo[i] += zuo[i-1];
you[i] += you[i-1];
}
f[0][0] = 1;
int tot = 0;
for(int i = 1; i <= m; i++)
{
for(int j = 0; j <= you[i]; j++)
{
f[i][j] =(f[i][j] + f[i-1][j])%mod;
if(j >= 1)f[i][j] = (f[i][j] + 1ll * f[i-1][j-1] * (you[i] - j + 1) % mod) % mod;
for(int k = zuo[i - 1] ; k < zuo[i]; k++)
{
f[i][j] = 1ll * f[i][j] * max(0,(i - j - k)) % mod;
}
}
}
cout << f[m][you[m]];
return 0;
}