nssl1305-最大值【dp,数学】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/89276136

正题


题目大意

求有多少个长度为 n n 且由 1 p 1\sim p 组成的序列满足在求最大值时交换了 k k 次。


解题思路

考虑 d p dp 预处理。

f i , j , k f_{i,j,k} 表示长度为 i i ,最大的数是 j j ,交换了 k k

显然有 f i , j , k = f i 1 , p , k 1 + f i 1 , j , k j ( p < j ) f_{i,j,k}=f_{i-1,p,k-1}+f_{i-1,j,k}*j(p<j)
对于 f i 1 , p , k 1 f_{i-1,p,k-1} 我们可以前缀和优化到 O ( n p k ) O(npk)

然后我们要考虑最大的不一定是 p p ,所以 a n s = i = 1 p f n , i , k ans=\sum _{i=1}^p f_{n,i,k}

时间复杂度 O ( N P K + p ) O(NPK+\sum p)


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll XJQ=1e9+7;
ll t,f[110][310][110],n,k,p,ans,sum[310][110];
int main()
{
	scanf("%lld",&t);
	for(ll i=1;i<=300;i++)
	  f[1][i][1]=1;
	for(ll j=1;j<=300;j++)
	  sum[j][1]=(sum[j-1][1]+f[1][j][1])%XJQ; 
	for(ll i=2;i<=100;i++)
	{
	    for(ll j=1;j<=300;j++)
	      for(ll k=1;k<=min(i,j);k++)
	    	  f[i][j][k]=(f[i-1][j][k]*j%XJQ+sum[j-1][k-1])%XJQ;
	    memset(sum,0,sizeof(sum));
		for(ll j=1;j<=300;j++)
		  for(ll k=1;k<=i;k++)
		    sum[j][k]=(sum[j-1][k]+f[i][j][k]%XJQ);
	}
	while(t--){
		scanf("%lld%lld%lld",&n,&k,&p);
		p++;ans=0;
		for(ll i=1;i<=k;i++)
		  ans=(ans+f[n][i][p])%XJQ;
		printf("%lld\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/89276136