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

    P2513 [HAOI2009]逆序对数列

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


    错误日志: 没想対, 菜是原罪, 最近状态不佳


    Solution

    在一段 (1 - (i - 1)) 的排列中加入 (i) 你可以控制 (i) 插入的位置, 给这个排列的逆序对任意加上 (1 - (i - 1)) 对(从最右到最左插入)
    于是想到状态 (dp[i][j]) 表示为考虑 (1 - i) 的排列, 逆序对数为 (j) 的方案数
    然后写出状态转移方程:$$dp[i][j] = sum_{k = 0}^{min(j, i - 1)}dp[i - 1][j - k]$$
    这样枚举 (k), 复杂度为 (O(nk^{2})) 会炸
    观察这个式子, 令 (t = j - k) ,换一下元, 交换 (sum) 的上下边界, 我们可以得到:$$dp[i][j] = sum_{t = max(0, j - i +1)}^{j}dp[i - 1][t]$$
    发现 (t) 的范围为一段可以维护和的区间, 前缀和维护即可

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #include<climits>
    #define LL long long
    #define REP(i, x, y) for(int i = (x);i <= (y);i++)
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 2019, M = 10000;
    int num, K;
    int dp[maxn][maxn];
    int main(){
    	num = RD(), K = RD();
    	dp[1][0] = 1;
    	REP(i, 2, num){
    		int sum = 0;
    		REP(j, 0, K){
    			sum = (sum + dp[i - 1][j]) % M;
    			if(j - i + 1 > 0)sum = (sum - dp[i - 1][j - i] + M) % M;
    			dp[i][j] = sum;
    			}
    		}
    	printf("%d
    ", dp[num][K]);
    	return 0;
    	}
    
  • 相关阅读:
    Spring攻略学习笔记(3.00)AOP核心概念和术语
    zoj 3494
    第43周星期五
    findBugs学习小结
    第42周星期日
    Cookie知识小结
    第42周星期三
    第42周星期六
    第43周星期四小结
    第43周星期二
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9860818.html
Copyright © 2011-2022 走看看