码迷,mamicode.com
首页 > 其他好文 > 详细

P2513 [HAOI2009]逆序对数列

时间:2018-10-27 14:58:57      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:cpp   数组   namespace   include   line   区间   tchar   组成   前缀和   

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\n", dp[num][K]);
    return 0;
    }

P2513 [HAOI2009]逆序对数列

标签:cpp   数组   namespace   include   line   区间   tchar   组成   前缀和   

原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9860818.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!