• 【JZOJ1857】最大值【dp】


    题目大意:

    题目链接:
    JZOJ:https://jzoj.net/senior/#main/show/1857
    学校局域网:http://10.156.31.134/contestnew.aspx?cid=91

    求在1m1sim m中选出nn个(可重复)最长严格上升子序列为p+1p+1的方案数。


    思路:

    由于TT比较大,考虑先预处理。
    f[i][j][k]f[i][j][k]表示前ii个数字,第ii个数字为jj,最长上升子序列长度为kk的方案数。
    显然需要枚举一个ll,表示第i1i-1位选择的数字。
    如果此次选择的数字是最长上升子序列的最后一位(也就是jj),那么第i1i-1位就可能是1j11sim j-1的任意一位。所以有
    f[i][j][k]+=f[i1][l][k1]f[i][j][k]+=f[i-1][l][k-1]
    如果此次选择的数字不是最长上升子序列的最后一位,那么在1i11sim i-1中最大值也是jj,这次选择的数字是在1j1sim j中的任意一个数字,那么就有
    f[i][j][k]+=f[i1][j][k]×jf[i][j][k]+=f[i-1][j][k] imes j
    但是这样的时间复杂度是O(n2k2)O(n^2k^2)的。
    考虑把枚举的ll去掉。
    由于我们要求
    l=1j1f[i1][k][k1]sum^{j-1}_{l=1}f[i-1][k][k-1]
    所以我们可以考虑使用前缀和优化。
    g[i][j][k]=l=1jf[i][j][k]g[i][j][k]=sum^{j}_{l=1}f[i][j][k],那么第一条方程就变成了
    f[i][j][k]+=g[i1][j1][k1]f[i][j][k]+=g[i-1][j-1][k-1]
    这样的时间复杂度就是O(n2k)O(n^2k)的了。
    接下来TT次询问,每次枚举一下最长上升子序列的最后一位是几,然后取最优输出即可。


    代码:

    #include <cstdio>
    using namespace std;
    typedef long long ll;
    
    const int MOD=1e9+7,N=100,M=300;
    ll f[N+10][M+10][N+10],g[N+10][M+10][N+10],ans;
    int T,n,m,p;
    
    int main()
    {
    	scanf("%d",&T);
    	for (int i=1;i<=M;i++)
    		f[1][i][1]=1,g[1][i][1]=i;
    	for (int i=2;i<=N;i++)
    		for (int j=1;j<=M;j++)
    			for (int k=1;k<=i;k++)
    			{
    				f[i][j][k]=(g[i-1][j-1][k-1]+f[i-1][j][k]*(ll)j)%MOD;
    				g[i][j][k]=(f[i][j][k]+g[i][j-1][k])%MOD;
    			}
    	while (T--)
    	{
    		scanf("%d%d%d",&n,&m,&p);
    		ans=0;
    		for (int i=p+1;i<=m;i++)
    			ans=(ans+f[n][i][p+1])%MOD;
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    java线程间的协作
    java线程间的共享
    java多线程基础API
    java并发编程基础概念
    如何设计一套规则引擎系统
    Stream—一个早产的婴儿
    Java函数式编程的前生今世
    关于微服务划分的一些思考
    如何更优雅的给控制器 “减负”
    PHP简洁之道
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998245.html
Copyright © 2020-2023  润新知