题目链接:点击这里
题目大意:
对于一个数列
a
i
{a_i}
ai,如果有
i
<
j
i<j
i<j 且
a
i
>
a
j
a_i>a_j
ai>aj ,那么我们称ai与aj为一对逆序对数。若对于任意一个由
1
−
n
1-n
1−n 自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为k的这样自然数数列到底有多少个?
题目分析:
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为前
i
i
i 个数的全排列中你逆序对为
k
k
k 的排列数
我们考虑放第
i
i
i 个数时,因为
i
i
i 为此数列的最大值,所以显然有如下转移方程:
d
p
[
i
]
[
j
]
=
∑
k
=
m
a
x
(
0
,
j
−
i
+
1
)
j
d
p
[
i
−
1
]
[
k
]
dp[i][j]=\sum_{k=max(0,j−i+1)}^jdp[i−1][k]
dp[i][j]=∑k=max(0,j−i+1)jdp[i−1][k]
此方程复杂度是
O
(
n
k
2
)
O(nk^2)
O(nk2) 的不够优秀,考虑优化
我们发现每次转移的区间都是一段连续区间,因此考虑有前缀和来优化这个方程的转移
记
s
u
m
sum
sum 为
∑
k
=
m
a
x
(
0
,
j
−
i
+
1
)
j
d
p
[
i
−
1
]
[
k
]
\sum_{k=max(0,j−i+1)}^jdp[i−1][k]
∑k=max(0,j−i+1)jdp[i−1][k] 当其区间长度不够
i
i
i 时,只需不断增加即可,当长度为到
i
i
i 时我们只要加一个减一个来维持区间长度不变即可
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
ll res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 1e3+5;
const int mod = 10000;
const double pi = acos(-1);
const double eps = 1e-8;
int n,k,dp[maxn][maxn],sum;
int main()
{
n = read(),k = read();
for(int i = 0;i <= n;i++)
dp[i][0] = 1;
for(int i = 1;i <= n;i++)
{
sum = 0;
for(int j = 0;j <= k;j++)
{
sum = (sum+dp[i-1][j])%mod;
dp[i][j] = sum;
if(j-i+1 >= 0) sum = ((sum-dp[i-1][j-i+1])%mod+mod)%mod;
}
}
printf("%d\n",dp[n][k]);
return 0;
}