P2513 [HAOI2009]逆序对数列

Posted tony-double-sky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2513 [HAOI2009]逆序对数列相关的知识,希望对你有一定的参考价值。

P2513 [HAOI2009]逆序对数列

题目描述
对于一个数列{ai},如果有iaj,那么我们称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;
    }

以上是关于P2513 [HAOI2009]逆序对数列的主要内容,如果未能解决你的问题,请参考以下文章

P2513 [HAOI2009]逆序对数列

逆序对数列(简单dp)

逆序对数列(简单dp)

[HAOI2009] 逆序对数列

BZOJ 2431: [HAOI2009]逆序对数列

[HAOI2009]逆序对数列