河南省选逆序对

96 阅读1分钟

今天写个dp的题解,wls的campdiv2 第一个dp

[HAOI2009]逆序对数列

题目描述

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

输入格式

第一行为两个整数n,k。

输出格式

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

样例 #1

样例输入 #1

4 1

样例输出 #1

3

提示

样例说明:

下列3个数列逆序对数都为1;分别是1 2 4 3 ;1 3 2 4 ;2 1 3 4;

测试数据范围

30%的数据 n<=12

100%的数据 n<=1000,k<=1000

分析

这题还是个比较简单的dp,我们考虑状态转移方程dp[i][j]dp[i][j]含义是所以1到n的全排列,逆序对数量是j的方案数,接着我们就想怎么计算状态,很显然当我们考虑i+1和i的关系就是我们可以把i+1插到原序列的任何一个位置,且插到0,1,2,3...i位置多的逆序对个数分别是i,i-1...0,于是我们可以得出转移方程dp[i+1][j]+=dp[i][k]dp[i+1][j]+=dp[i][k],kkmax(0,ji)max(0,j-i)jj之间,于是我们可以写出一个O(nk2)O(nk^2)的一个时间复杂度,很显然,会tle,但是在洛谷开O2优化可以过。 于是我们开始想怎么优化,很显然要前缀和优化,然后注意一下取模就ok了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1010;
const int mod=10000;
int dp[N][N];
int n,k;
int main(){
	cin>>n>>k;
	for(int i=0;i<=n;i++) dp[i][0]=1;
	for(int i=0;i<n;i++){
		for(int j=1;j<=k;j++){
			dp[i][j]=(dp[i][j]+dp[i][j-1]+mod)%mod;
			if(j-i<=0)
			    dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
			else dp[i+1][j]=(dp[i+1][j]+dp[i][j]-dp[i][j-i-1]+100*mod)%mod;
		}
	}
	cout<<dp[n][k]<<endl;
}