• 「BZOJ2431」 [HAOI2009]逆序对数列 DP


    problem

    Description

    对于一个数列({ai}),如果有(i<j)(a_i>a_j),那么我们称(a_i)(a_j)为一对逆序对数。若对于任意一个由(1...n)自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为k的这样自然数数列到底有多少个?

    Input

    第一行为两个整数(n)(k)

    Output

    写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对(10000)求余数后的结果。

    Sample Input

    4 1
    

    Sample Output

    3
    

    Hint

    (n<=1000,k<=1000)

    Solution

    考虑一个(1...i)的排列,当要插入(i+1)时,由于它是最大的,所以无论在何处插入,其后方的所有数都会与它产生一对新的逆序对

    于是有方程(dp[i][j])表示(1...i)的排列中,有(j)个逆序对的方案数

    转移方程为:

    [dp[i][j]=sum_{k=0}^{min(i-1,j)}dp[i-1][j-k] ]

    边界为(dp[1][0]=1)

    复杂度是(O(nk^2)),注意到可以利用前缀和优化,于是使得复杂度进一步下降为(O(nk)),复杂度正确

    Code

    实际实现过程中发现状态数组可以优化掉第一维

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define maxn 1005
    using namespace std;
    
    const int mod=10000;
    int n,K;
    int dp[maxn];
    int sum[maxn];
    
    int main()
    {
    	scanf("%d%d",&n,&K);
    	dp[0]=sum[0]=1;
    //	for(register int i=1;i<=n;++i)
    //		for(register int j=0;j<=K;++j)
    //			for(register int k=min(j,i-1);k>=0;--k)
    //				dp[i][j]=(dp[i][j]+dp[i-1][j-k])%mod;
    	for(register int i=1;i<=K;++i)
    		sum[i]=sum[i-1];
    	for(register int i=2;i<=n;++i)
    	{
    		for(register int j=0;j<=K;++j)
    		{
    			dp[j]=sum[j]%mod;
    			if(j>i-1)
    				dp[j]=(dp[j]-sum[j-i]+mod)%mod;
    		}
    		sum[0]=dp[0];
    		for(register int j=1;j<=K;++j)
    			sum[j]=(sum[j-1]+dp[j])%mod;
    	}
    	printf("%d",dp[K]);
    }
    

    这大概是处理类似的排列或者计数一类题目的套路?
    似乎很值得推广啊

  • 相关阅读:
    PLSQL13
    01.Spring引入
    验证码重构
    短信验证码登录思路
    记住我 token保存到数据库
    图形验证码及其重构
    个性化用户认证流程
    01.Spring Security初识,表单认证
    堆和栈的区别
    系统分析与设计第二次作业
  • 原文地址:https://www.cnblogs.com/lizbaka/p/10292116.html
Copyright © 2020-2023  润新知