JSZKC is the captain of the lala team.
There are N girls in the lala team. And their height is [1,N] and distinct. So it means there are no two girls with a same height.
JSZKC has to arrange them as an array from left to right and let h[i] be the height of the ith girl counting from the left. After that, he can calculate the sum of the inversion pairs. A inversion pair counts if h[i]>h[j] with inode
The input file contains several test cases, each of them as described below.
The first line of the input contains two integers N and K (1 ≤ N ≤ 5000, 0 ≤ K ≤ 5000), giving the number of girls and the pairs that JSZKC asked.
There are no more than 5000 test cases.ios
An integer in one line for each test case, which is the number of the plans mod 1000000007.數組
3 2 3 3
3 2 3 3
2 1
2 1
問你1~n
的全部排列中有多少種排列擁有k
對逆序數。ide
分析:優化
dp[i][j]表明長度爲ii的排列有jj對逆序數的方案數,考慮放第ii個數的時候,前面i−1i−1個數的全部方案都已知,且都比ii小,若是ii放在前i−1i−1個數的最左邊,則會新產生i−1i−1對逆序數,若是ii放在前i−1i−1個數的最右邊,則不會產生逆序數。也就是說在前i−1i−1個數已經固定,準備放置第ii個數時,能夠產生的逆序數對的數量x∈[0,i−1]x∈[0,i−1],因而有:spa
題目只給了64MB64MB說的內存,因此須要把詢問離線下來,而後用滾動數組求解同時離線答案。debug
AC代碼:code
#include <map> #include <set> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #define debug(a) cout << #a << " " << a << endl using namespace std; const int maxn = 5*1e3; const int mod = 1e9 + 7; typedef long long ll; ll dp[3][maxn+10], ans[maxn+10], cnt = 0, cur = 0; struct node { ll n, k, index; }; node query[maxn+10]; bool cmp( node p, node q ) { return p.n < q.n; } int main() { std::ios::sync_with_stdio(false); while( cin >> query[cnt].n >> query[cnt].k ) { query[cnt].index = cnt; cnt ++; } sort( query, query+cnt, cmp ); dp[0][0] = 1; for( ll i = 1; i <= maxn; i ++ ) { ll sum = 0; for( ll j = 0; j <= maxn; j ++ ) { sum = ( sum + dp[i-1&1][j] ) % mod; if( j-i >= 0 ) { sum = ( sum - dp[i-1&1][j-i] + mod ) % mod; } dp[i&1][j] = sum; } while( cur < cnt && query[cur].n == i ) { ans[query[cur].index] = dp[i&1][query[cur].k]; cur ++; } } for( ll i = 0; i < cnt; i ++ ) { cout << ans[i] << endl; } return 0; }
最後貼一下關於滾動數組的知識:blog
滾動數組的做用在於優化空間,主要應用在遞推或動態規劃中(如01揹包問題)。由於DP題目是一個自底向上的擴展過程,咱們經常須要用到的是連續的解,前面的解每每能夠捨去。因此用滾動數組優化是頗有效的。利用滾動數組的話在N很大的狀況下能夠達到壓縮存儲的做用。ip
一個簡單的例子:
斐波那契數列:
int main() { int i; long long d[80]; d[0] = 1; d[1] = 1; for(i = 2; i < 80; i++) { d[i] = d[i - 1] + d[i - 2]; } printf("%lld\n",d[79]); return 0; }
上面這個循環d[i]只依賴於前兩個數據d[i - 1]和d[i - 2]; 爲了節約空間用滾動數組的作法。
int Fib[3]; int fib(int n) { Fib[1] = 0; Fib[2] = 1; for(int i = 2; i <= n; ++i) { Fib[0] = Fib[1]; Fib[1] = Fib[2]; Fib[2] = Fib[0] + Fib[1]; } return Fib[2]; }
同
int main() { int i; long long d[3]; d[0] = 1; d[1] = 1; for(i = 2; i < 80; i++) { d[i % 3] = d[(i - 1) % 3] + d[(i - 2) % 3]; } printf("%lld\n", d[79%3]); return 0; }
上面的取餘運算,咱們成功地只保留了須要的最後3個解,數組好象在「滾動」同樣,因此叫滾動數組(對於二維也能夠用)。
因此,很明顯,滾動數組能夠經過取餘(%)來實現的,(實現一個滾動|循環)
可是這裏存在一個通病,那就是時間換內存必定會犧牲時間。所以,滾動數組通常用在時間比較充裕,而內存不夠的狀況下。
滾動數組實際是一種節省空間的辦法,時間上沒啥優點,多用於DP中,舉個例子吧:
一個DP,日常若是須要1000×1000的空間,其實根據DP的無後效性,能夠開成2×1000,而後經過滾動,得到和1000×1000同樣的效果。滾動數組經常使用於DP之中,在DP過程當中,咱們在由一個狀態轉向另外一個狀態時,極可能以前存儲的某些狀態信息就已經無用了,例如在01揹包問題中,從理解角度講咱們應開DP[i][j]的二維數組,第一維咱們存處理到第幾個物品,也就是階段了,第二維存儲容量,可是咱們得到DP[i],只需使用DP[i - 1]的信息,DP[i - k],k>1都成了無用空間,所以咱們能夠將數組開成一維就行,迭代更新數組中內容,滾動數組也是這個原理,目的也同樣,不過這時候的問題經常是不可能縮成一維的了,好比一個DP[i][j]須要由DP[i - 1 ][k],DP[i - 2][k]決定,i<n,0<k<=10;n <= 100000000;顯然縮不成一維,正常咱們應該開一個DP[100000005][11]的數組,結果很明顯,超內存,其實咱們只要開DP[3][11]就夠了DP[i%3][j]由DP[(i - 1)%3][k]和DP[(i - 2)%3][k]決定,空間複雜度差異巨大。