[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;
} 
posted @ 2018-10-23 09:48  风浔凌  阅读(501)  评论(0编辑  收藏  举报