zoukankan      html  css  js  c++  java
  • 题解 P2513 【[HAOI2009]逆序对数列】

    大概就是P1527的加强版?

    由于这个问题里面是1-n的全排列,所以对于动态规划的转移便变得十分方便。(总感觉似曾相识?)

    我们发现,我们往已有的序列中添加一个数,这个数一定大于已有序列中的所有数,所以如果放在最后,逆序对数量增加了0,放在倒数第二的位置,逆序对增加了1,如果放在了最前面则逆序对数量增加原序列的长度。

    所以我们简单地定义状态为长度为(i)的序列中,混乱度为(j)的序列个数。状态转移方程为:

    [dp[i][j]+=dp[i-1][j-k](0<=k<=i-1&j-k>=0) ]

    (好像也似曾相识?)

    最简单的版本很好想,这里不再放出代码,然后我们学习将原本(O(n^2m))复杂度的算法利用前缀和加速成(O(nm))

    我们发现,当前状态可以被一个连续的状态段摸到,也就是说,对于当前状态的更新需要加上一个连续的状态段的和,对于在(O(1))时间里读取连续段的和,非常自然的,利用前缀和保存(i-1)的状态的前缀和。

    不过,由于这个更新方程的特殊性,如果只使用一条滚动数组,更新前缀和值的时候会覆盖掉后面可能还需使用的值,如果倒序推导状态的话又对前缀和的导出极其不友好,可能需要另外循环一遍来导出前缀和,于是我采用了利用两个滚动数组反复横跳的方式来避免上述问题。(我考试的数据要是不用滚动数组会MLE)

    代码如下。

    #include<bits/stdc++.h>
    #define max(a,b) (a>b?a:b)
    long long dp[2][5005],pre[2][5005],n,c;//滚动数组储存状态和前缀和。
    bool p;
    int main(){
        std::cin>>n>>c;
        dp[1][1]=1;//显然的,长度为1逆序对为0的个数为1。
        for(int j=1;j<=c+1;j++)
            pre[1][j]=1;//初始化
        for(int i=2;i<=n;i++){
            for(int j=1;j<=c+1;j++){ //为方便前缀和,我把逆序对数量都往上加了1
                dp[p][j]=(pre[!p][j]-pre[!p][max(j-i,0)]+10000)%10000;//调用前缀和
                pre[p][j]=(pre[p][j-1]+dp[p][j]+10000)%10000;//防止玄学模运算出现玄学错误。
            }//记录前缀和
            p=!p;//反复横跳
        }
        std::cout<<dp[!p][c+1];
    }
    
  • 相关阅读:
    班服 状压DP NOIP模拟赛
    记录奥林比克/课程录制 洛谷P2255 [USACO14JAN]
    字符串的展开 vijos1379 NOIP2007 字符串 模拟
    树网的核 Vijos1362 NOIP2007 树结构 直径 暴搜
    浏览器(Web Navigation) codevs 5373 POJ 1028 简单模拟
    FPS集合 Codgic1351 动态规划 DP NOIP模拟赛
    基于VBA的Excel抽奖软件
    【CodeForces】Round #436
    【NOI OpenJudge】【1.3】编程基础之算术表达式与顺序执行
    【NOI OpenJudge】【1.2】编程基础之变量定义、赋值及转换
  • 原文地址:https://www.cnblogs.com/Schwarzkopf-Henkal/p/11775580.html
Copyright © 2011-2022 走看看