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

D e s c r p t i o n Descrption Descrption

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

T ≤ 10000 , p &lt; n ≤ 100 , k ≤ 300 T\leq 10000,p&lt;n\leq 100,k\leq 300 T10000,p<n100,k300


S o l u t i o n Solution Solution

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

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

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

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

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

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

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

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


C o d e Code 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);
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值