作者:若我倆有了愛 | 来源:互联网 | 2023-06-03 11:39
题目链接
近两天都在写一个C++的Biginteger类,一直没刷题,今天补一下。
解法1.纯暴力解法 枚举所有全排列并求其逆序对,即使采用树状数组等高效方式求逆序对仍要O(nlogn∗n!)O(nlogn*n!)O(nlogn∗n!),完全不可行。
解法2.动态规划
定义dp[i][j]为前i个数的全排列中逆序对为j的个数。我一开始是考虑最后一个数字,但压根没有思路。
正确方法是:考虑有i-1个数的序列,将第i个数插入,由于它是序列中最大的,所以插入其中最后一个位置,可以新增0个逆序对;插入倒数第二个位置新增1个……以此类推,在最前面可以新增i-1个。(也就是说,最多可以新增i-1个)。
因此,dp[i][j]=dp[i-1]从第j-i-1个到第j个的和。
可以进行一个小小的优化:不难发现当j>(i−1)(i−2)2j>\frac{(i-1)(i-2)}{2}j>2(i−1)(i−2)时,dp[i-1]从(i−1)(i−2)2\frac{(i-1)(i-2)}{2}2(i−1)(i−2)到j的部分都是0,因此枚举到前者即可。即
dp[i][j]=∑k=max(0,j−i+1)min(j,cnt(x))dp[i−1][k]dp[i][j]=\sum \limits_{k=max(0,j-i+1)}^{min(j,cnt(x))} dp[i-1][k]dp[i][j]=k=max(0,j−i+1)∑min(j,cnt(x))dp[i−1][k],其中cnt(x)定义见以下代码:
#include
#include
#define cnt(x) (((x)*(x-1))/2)
#define MOD 10000
const int M=1001;
using namespace std;
int dp[M][M]={{0},{1,0}};
int main() {int n,k;scanf("%d%d",&n,&k);for(int i&#61;2; i<&#61;n; i&#43;&#43;) dp[i][0]&#61;1;for(int i&#61;2; i<&#61;n; i&#43;&#43;) {for(int j&#61;1; j<&#61;k; j&#43;&#43;) {for(int k&#61;max(0,j-i&#43;1); k<&#61;cnt(i-1)&&k<&#61;j; k&#43;&#43;) {dp[i][j]&#61;(dp[i][j]&#43;dp[i-1][k])%MOD;}}}printf("%d",d[n][k]);return 0;
}
其实做到这里&#xff0c;就不难想到用前缀和优化了。但要注意过程中可能出现负数&#xff0c;为了方便取模&#xff0c;这时就不得不将其调整为正数&#xff0c;而且结果dp[n][k]-dp[n][k-1]仍可能为负数&#xff0c;也需要做调整。
#include
#include
#define cnt(x) (((x)*(x-1))/2)
#define MOD 10000
const int M&#61;1001;
using namespace std;
int dp[M][M]&#61;{{0},{1,0}};
int main() {int n,k;scanf("%d%d",&n,&k);for(int i&#61;2; i<&#61;n; i&#43;&#43;) dp[i][0]&#61;1;for(int i&#61;2; i<&#61;n; i&#43;&#43;) {for(int j&#61;1; j<&#61;k; j&#43;&#43;) {int right&#61;min(cnt(i-1),j);if(j-i&#43;1<&#61;0) dp[i][j]&#61;(dp[i][j-1]&#43;dp[i-1][right])%MOD;else dp[i][j]&#61;(dp[i][j-1]&#43;(dp[i-1][right]-dp[i-1][j-i]&#43;MOD)%MOD)%MOD; }}printf("%d",((dp[n][k]-dp[n][k-1])&#43;MOD)%MOD); return 0;
}