洛谷 P1521 求逆序对

题目描述

我们说(i,j)是a1,a2,…,aN的一个逆序对当且仅当i<j且ai>a j。例如2,4,1,3,5的逆序对有3个,分别为(1,3),(2,3),(2,4)。现在已知N和K,求1…N的所有特定排列,这些排列的逆序对的数量恰好为K。输出这些特定排列的数量。

例如N=5,K=3的时候,满足条件的排列有15个,它们是:

1,2,5,4,3 1,3,4,5,2 1,3,5,2,4 1,4,2,5,3 1,4,3,2,5 1,5,2,3,4 2,1,4,5,3 2,1,5,3,4 2,3,1,5,4 2,3,4,1,5

2,4,1,3,5 3,1,2,5,4 3,1,4,2,5 3,2,1,4,5 4,1,2,3,5

输入输出格式

输入格式:

输入第一行有两个整数N和K。(N≤100,K≤N*(N-1)/2)

输出格式:

将1…N的逆序对数量为K的特定排列的数量输出。为了避免高精度计算,请将结果mod 10000以后再输出!

输入输出样例

输入样例#1:
5 3
输出样例#1:
15
先搜索。。。
ngs(k的最大值) n:f(k為0,1,2,3...)
 0  0:1
 0  1:1
 1  21,1
 3  3:1,2, 2
 6  4:1,3, 5,  6
10  5:1,4, 9, 15, 20, 22
15  6:1,5,14, 29, 49, 71,  90,  101, 101
21  7:1,6,20, 49, 98,169, 259,  359, 455,  531,  573,  573
28  8:1,7,27, 76,174,343, 602,  961,1415, 1940, 2493, 3017, 3450, 3736, 3836
36  9:1,8,35,111,285, 628,1230,2191,3606, 5545, 8031,11021,14395,17957,21450, 24584, 27073, 28675, 29228
45 10:1,9,44,155,440,1068,2298,4489,8095,13640,21670,32683,47043,64889,86054,110010,135853,162337,187959,211089,230131,243694,250749,250749
(註:應該是對稱的,為了不超行,我刪掉了後面的。)
看了一下上面的,相信聰明的,你,一定發現了規律。(呵呵,不可能的!)
if(j<=ngs[i]/2){
f[i][j]=f[i-1][j]+f[i][j-1];
if(j>=i) f[i][j]-=f[i-1][j-i];
}
else f[i][j]=f[i][ngs[i]-j];
呵呵,是不是很神奇。
因為數值很大,需要mod,這裡有一些小技巧。
代碼實現:
 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int mod=10000;
 5 int n,k,ngs[120],f[120][6000];
 6 int main(){
 7 scanf("%d%d",&n,&k);f[0][0]=f[1][0]=1;//初始化。
 8 for(int i=2;i<=n;i++){
 9 ngs[i]=ngs[i-1]+i-1;//你懂的。
10 for(int j=0;j<=ngs[i];j++){
11 f[i-1][j]%=mod;//mod的小技巧。
12 if(j<=ngs[i]/2){//DP方程應用。
13 f[i][j]=f[i-1][j]+f[i][j-1];
14 if(j>=i) f[i][j]-=f[i-1][j-i];
15 }
16 else f[i][j]=f[i][ngs[i]-j];
17 }
18 }
19 printf("%d
",f[n][k]%mod);//mod的小技巧。
20 return 0;
21 }
View Code

這絕不是一道搜索題~

原文地址:https://www.cnblogs.com/J-william/p/6043650.html