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;
} 

猜你喜欢

转载自blog.csdn.net/Bluelanzhan/article/details/83512572