Description
对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数。若对于任意一个由1~n自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为k的这样自然数数列到底有多少个?
Input
第一行为两个整数n,k。
Output
写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果。
Sample Input
4 1
Sample Output
3
Hint
样例说明:
下列3个数列逆序对数都为1;
分别是1 2 4 3 ;1 3 2 4 ;2 1 3 4;
测试数据范围
30%的数据 n<=12
100%的数据 n<=1000,k<=1000
Source
HAOI,分治,递推
Solution
考虑DP。
dp[i][j]表示i的排列中逆序对数为j的方案数。
考虑i的放置,i为最大值,所以放在i-1个位置都可以计算出对答案的贡献,因此DP递推式为:
dp[i][j]=Σdp[i-1][k] (j-i+1<=k<=j)
考虑特殊情况:到i时最多可以贡献i-1对逆序对,所以从dp[0]~dp[j-i+1]这一段不能加!
但是这个算法要枚举i、j和k,时间复杂度为n^3,绝对TLE,因此考虑前缀和优化。
Code
1 #include <bits/stdc++.h> 2 #define int long long 3 4 using namespace std; 5 6 inline int read() 7 { 8 int f = 1, x = 0; 9 char c = getchar(); 10 11 while (c < '0' || c > '9') 12 { 13 if (c == '-') 14 f = -1; 15 c = getchar(); 16 } 17 18 while (c >= '0' && c <= '9') 19 { 20 x = x * 10 + c - '0'; 21 c = getchar(); 22 } 23 24 return f * x; 25 } 26 27 const int mod = 10000; 28 29 int n, m, dp[1005][1005]; 30 31 signed main() 32 { 33 n = read(), m = read(); 34 35 dp[1][0] = 1; 36 37 for (register int i = 2; i <= n; i++) 38 { 39 int sum = 0; 40 41 for (register int j = 0; j <= m; j++) 42 { 43 sum = (sum + dp[i - 1][j]) % mod; 44 45 dp[i][j] = sum; 46 47 if (j - i + 1 >= 0) 48 { 49 sum = (sum - dp[i - 1][j - i + 1] + mod) % mod; 50 } 51 } 52 } 53 54 printf("%lld", dp[n][m]); 55 56 return 0; 57 }