[HAOI2009] 逆序对数列
观察数据范围我们发现可以用DP来做这个题qwq,因为它每一位往上填的时候,无论怎么填,总可以从前面的状态转移过来。
而且题目保证了是从1-n的自然数,所以不存在相同数字的情况。
我们设计状态\(dp[i][j]\)为长度为i的序列(也就是前\(i\)个自然数)排成的序列中逆序对数量为\(j\)的答案个数。
之后的转移就是往原先的序列中塞下一个数,填的位置不同自然增加的逆序对数量不一样,但是显然增加的数量只能在\(0\)到\({i-1}\)中。
首先我们可以考虑暴力的\(n^3\)做法。
转移方程:
\[dp[i][j]=\sum_{k=0}^{max(j-i+1,0)}dp[i-1][k]
\]
试了试竟然有90分???
但是显然1000的数据不是让我们用\(n^3\)闹着玩的。所以我们要试图将它优化到\(n^2\)。这个优化很容易,打个表我们稍加思考就可以发现,每次加的数有重复,而且是以前缀和的形式出现的。所以我们直接记录前缀和做优化就可以省略掉k的遍历了。
开\(sum[i][j]\)来记录前i个数前j个逆序的答案个数。。。。就行了吧qwq
代码如下。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define mod 10000
using namespace std;
int n,k;
int dp[1010][1010],sum[1010][1010];
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<=n;i++) dp[i][0]=1,sum[i][0]=1;
sum[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
dp[i][j]=(dp[i][j]+sum[i-1][j])%mod;
if(j-i>=0) dp[i][j]=(dp[i][j]+mod-sum[i-1][j-i])%mod;
sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
}
}
printf("%d\n",dp[n][k]);
return 0;
}