牛客练习赛71 数学考试(两种dp方式+容斥思想)

这道题可以说是对 d p dp dp的考察非常特别

好题,但是我是废物,一种做法都没想到


Ⅰ . 考 虑 到 前 i 个 数 不 能 是 [ 1 , i ] 的 排 列 \color{Red}Ⅰ.考虑到前i个数不能是[1,i]的排列 .i[1,i]

等 价 于 前 i 个 数 出 现 过 大 于 i 的 数 字 等价于前i个数出现过大于i的数字 ii

定 义 d p [ i ] [ j ] 为 前 i 个 数 字 种 最 大 是 j 的 排 列 数 字 定义dp[i][j]为前i个数字种最大是j的排列数字 dp[i][j]ij

转移是 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ ( i − j + 1 ) + p r e [ j − 1 ] dp[i][j]=dp[i-1][j]*(i-j+1)+pre[j-1] dp[i][j]=dp[i1][j](ij+1)+pre[j1]

p r e [ j − 1 ] = ∑ i = 1 j − 1 d p [ i − 1 ] [ i ] pre[j-1]=\sum\limits_{i=1}^{j-1}dp[i-1][i] pre[j1]=i=1j1dp[i1][i]

这部分表示若前 i − 1 i-1 i1个数字不大于 j j j,现在就可以放数字 j j j来转移

d p [ i − 1 ] [ j ] ∗ ( j − i + 1 ) dp[i-1][j]*(j-i+1) dp[i1][j](ji+1)

这部分表示若前 i − 1 i-1 i1个数的最大值是 j j j,说明还有 j − ( i − 1 ) j-(i-1) j(i1)个小于 j j j的没用过

这样最后 d p [ n ] [ n ] dp[n][n] dp[n][n]就是答案

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2009;
const int mod=20000311;
int p[maxn],dp[maxn][maxn],pre[maxn];
signed main()
{
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++)	cin >> p[i];
	memset(dp,-1,sizeof(dp));
	for(int i=1;i<=n;i++)	dp[1][i]=1;
	for(int i=1;i<=m;i++)	dp[p[i]][p[i]]=0;
	for(int i=1;i<=n;i++)	pre[i]=pre[i-1]+dp[1][i];
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if( dp[i][j]==0 )	continue;
			if( i>j )	dp[i][j]=0;
			else
				dp[i][j] = ( dp[i-1][j]*(j-i+1)+pre[j-1] )%mod;
		}
		for(int j=1;j<=n;j++)
			pre[j] = (pre[j-1]+dp[i][j] )%mod;
	}
	printf("%lld",dp[n][n]);
} 

第 二 种 做 法 是 一 维 的 , 也 很 巧 妙 \color{Red}第二种做法是一维的,也很巧妙 ,

定义 d p [ i ] dp[i] dp[i]是处理到第 p [ i ] p[i] p[i]个数,前 i − 1 i-1 i1个限制满足条件,第 i i i个限制条件不满足

这样就可以做到不重不漏

最后用 n ! − ∑ i = 1 m d p [ i ] n!-\sum\limits_{i=1}^{m}dp[i] n!i=1mdp[i]就是答案

d p [ 1 ] = f a c [ p [ 1 ] ] dp[1]=fac[p[1]] dp[1]=fac[p[1]]很明显,就是 [ 1 , p [ 1 ] ] [1,p[1]] [1,p[1]]的全排列

那么 d p [ 2 ] = f a c [ p [ 2 ] ] dp[2]=fac[p[2]] dp[2]=fac[p[2]]对吗?算多了,需要减掉在第一个限制条件就不满足的情况

就是 d p [ 2 ] = f a c [ p [ 2 ] ] − d p [ 1 ] ∗ f a c [ p [ 2 ] − p [ 1 ] ] dp[2]=fac[p[2]]-dp[1]*fac[p[2]-p[1]] dp[2]=fac[p[2]]dp[1]fac[p[2]p[1]]

意思是 [ 1 , p [ 1 ] ] [1,p[1]] [1,p[1]]的数字仍然在 [ 1 , p [ i ] ] [1,p[i]] [1,p[i]]位置,余下来的位置随便排列

接下来就是一样的道理了

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2009;
const int mod=20000311;
int dp[maxn],p[maxn],fac[maxn];
signed main()
{
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++)	cin >> p[i];
	fac[0]=1;
	for(int i=1;i<=n;i++)	fac[i]=fac[i-1]*i%mod;
	sort(p+1,p+1+m);
	dp[1]=fac[p[1]];
	for(int i=2;i<=m;i++)
	{
		dp[i]=fac[p[i]];
		for(int j=1;j<i;j++)
			dp[i] = ( dp[i]-dp[j]*fac[p[i]-p[j]] )%mod;
	}
	int ans=fac[n];
	for(int i=1;i<=m;i++)	ans = ( ans-dp[i]*fac[n-p[i]] )%mod;
	cout << ( ans%mod+mod )%mod;
}