2019.4.7 提高B组 T2 nssl-1305 最大值

版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/89155006

D e s c r p t i o n Descrption

T T 组询问,每次询问长度为 n n ,每个数的范围都在 [ 1 k ] [1\sim k] L C S LCS (最长上升子序列,在本题中严格单调)的长度为 p + 1 p+1 的合法方案数,答案对 1 0 9 + 7 10^9+7 取模

T 10000 , p < n 100 , k 300 T\leq 10000,p<n\leq 100,k\leq 300


S o l u t i o n Solution

这题是一道比较显然的 d p dp ,我是在考场时就想出方程,然后并且成功拿到了30,后来预处理一个前缀和就 A A

f [ i ] [ j ] [ l ] f[i][j][l] 表示长度为 i i 的序列, L C S LCS 的末尾数组为 j j L C S LCS 的长度为 l l 时的方案数

边界: f [ 1 ] [ i ] [ 1 ] = 1 f[1][i][1]=1 ,表示长度为1的序列填 i i 这个数字 L C S LCS 的长度都为1,方案也只有1种

如果这一位填了 j j L C S LCS 长度不变,显然上一位是有 j j 种选择的,即填 [ j j + j 1 ] [j\sim j+j-1] f [ i ] [ j ] [ l ] = f [ i 1 ] [ j ] [ l ] × j f[i][j][l]=f[i-1][j][l]\times j

如果这一位填了 j j ,导致 L C S LCS 长度发生了变化,那么这时我们就要枚举一个 o o ,表示上一次选的数字,此时 f [ i ] [ j ] [ l ] = f [ i 1 ] [ o ] [ l 1 ] f[i][j][l]=\sum f[i-1][o][l-1]

回答时 a n s = i = p + 1 k f [ n ] [ i ] [ p + 1 ] ans=\sum_{i=p+1}^k f[n][i][p+1]

这样子复杂度 O ( n 2 k 2 + T k ) O(n^2k^2+Tk)

我们发现, o o 是一段连续的数字,也就是我们加的 f [ i 1 ] [ o ] [ l 1 ] f[i-1][o][l-1] 是可以用前缀和表示的,这样我们的时间复杂度就跌到了 O ( n 2 k + T k ) O(n^2k+Tk)


C o d e Code

#include<cstdio>
#include<cctype>
#include<cstring>
#define ri int
#define WYC 1000000007
using namespace std;int t,n,k,p;
long long f[110][313][110],ans,sum[101][101][301];
inline int read()
{
	char c;int d=1,f=0;
	while(!isdigit(c=getchar())) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(isdigit(c=getchar())) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
signed main()
{
	t=read();
	for(ri i=1;i<=300;i++) f[1][i][1]=1,sum[1][1][i]=i;
	for(ri i=2;i<=100;i++)
	 for(ri l=1;l<=i;l++)
	  for(ri j=l;j<=300;j++)
	{
		(f[i][j][l]+=f[i-1][j][l]*j)%=WYC;
		(f[i][j][l]+=sum[i-1][l-1][j-1])%=WYC;
		(sum[i][l][j]=sum[i][l][j-1]+f[i][j][l])%=WYC;
	}
	while(t--)
	{
		n=read();k=read();p=read();
		ans=0;
		for(ri i=p+1;i<=k;i++) (ans+=f[n][i][p+1])%=WYC;
		printf("%lld\n",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/89155006
今日推荐