BZOJ2431 逆序對數列 KEY

2021-08-04 07:53:23 字數 1518 閱讀 7141

題目傳送門

不知道今天是怎麼了,可能是空調吹多了吧,一直不在狀態,連遞推題我都做不來了……(扎zn了老fe……)

然而,不管環境如何惡劣,我們仍要努力學習,為了自己的明天而奮鬥。(說的好像跟真的一樣)

其實這題就是乙個遞推,現在我們考慮第i個數,定義f[i][j]表示序列裡有i個數,逆序對的組數為j的方案數。

因為第i個數的權值就是i,則不管第i個數插到序列裡的哪個位置,都會和在它後面的數形成逆序對,因此第i個數插到序列裡最多形成i-1個逆序對,最少形成0個。

所以,我們就得到了遞推公式:f[i][j]=σf[i-1][j-k] (j-i+1<=k<=j)

但是現在的時間複雜度仍然是o(n^3)的,n的範圍是1000,鐵定tle。

但是看到上面的遞推式中有σ,於是我們就想到了字首和,降掉一維的複雜度,過掉這道題非常輕鬆。

另外,由遞推式可發現,第i個數的所有逆序對方案數都只跟第i-1個數的逆序對方案數有關,因此可以使用滾動陣列來儲存,減少記憶體的使用。

(雖然在這題裡並沒有什麼卵用,在bzoj上實測出來大概省了80+kb的空間吧……)

注意:本題需要考慮中途答案為負的情況,雖然只要加上p就行了,但是一定要注意考慮,別忘了。

o(n^3)演算法(主要是怕自己會忘):

#include 

#define p 10000

using

namespace

std;

int n,m,f[1010][1010];

int main(void)

o(n^2)演算法(ac**):

#include 

#define p 10000

using

namespace

std;

int n,m,f[1010],c[1010];

int main(void)

printf("%d",f[m]);

return

0;

}

附:公式改進法,我在洛谷上看見的。

由上面的那個遞推公式可知:f[i][j]=f[i-1][j]+f[i-1][j-1]+…+f[i-1][j-i+1]

又f[i][j-1]=f[i-1][j-1]+f[i-1][j-2]+…+f[i-1][j-i]

所以f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-i]

雖然這個公式的變形在這題中並沒有什麼特別大的用處,但是這種思想是非常好的,常常可以把一些非常複雜的公式變得簡單些,公式的特點也更明顯一些。

所以我們還是有必要學習一下這種思想的。

然後就是遞推了,其他都和上面的**差不多的。

#include 

#define p 10000

using namespace std;

int n,m,f[2][10010];

int main(void)

BZOJ 2431 逆序對數列

time limit 5 sec memory limit 128 mb submit 2611 solved 1526 submit status discuss description 對於乙個數列,如果有i aj,那麼我們稱ai與aj為一對逆序對數。若對於任意乙個由1 n自然數組成的 數列,可...

BZOJ 2431 逆序對數列 DP

2431 haoi2009 逆序對數列 time limit 5 sec memory limit 128 mb description 對於乙個數列,如果有i j且ai aj,那麼我們稱ai與aj為一對逆序對數。若對於任意乙個由1 n自然數組成的數列,可以很容易求出有多少個逆序對數。那麼逆序對數為...

BZOJ2431 逆序對數列(動態規劃)

對於乙個數列,如果有i j且ai aj,那麼我們稱ai與aj為一對逆序對數。若對於任意乙個由1 n自然數組成的數列,可以很容易求出有多少個逆序對數。那麼逆序對數為k的這樣自然數數列到底有多少個?第一行為兩個整數n,k。寫入乙個整數,表示符合條件的數列個數,由於這個數可能很大,你只需輸出該數對1000...