排隊
阿新 • • 發佈:2020-07-10
題目大意
*小花所在的班有 n名同學(任何兩位同學身高不相同),正準備排成一列縱隊,但他們不想按身高從矮到高排,那樣太單調,太沒個性。
*他們希望恰好有 k對同學是高的在前,矮的在後,其餘都是矮的在前,高的在後。如當 n=5,k=3 時,假設5 人從矮到高分別標為 1,2,3,4,5,則 (1,5,2,3,4),(2,3,1,5,4),(3,1,4,2,5)都是可行的排法。
*小花想知道總共有多少種可行排法。
輸入格式
- 一行兩個整數 n 和 k,意義見問題描述。
輸出格式
- 輸出一個整數,表示可行排法數。由於結果可能很大,請輸出排法數 mod 1799999 的值。
樣例
樣例輸入
5 3
樣例輸出
15
演算法分析:
- 這nm是dp?
- 這nm還真是dp(以為是數論直接跳了……)
- 這個是可以用遞推做出來的 用f[i][j]表示前i個人有j對逆序對(不懂的去百度吧)
- 初始化f[i][0] = 1(即正好是順序排列)
- 然後我們去推第i個數 想象如果第i個數最大放在最後面 就一個不會加 放在倒數第二個呢 就會加一個 依次類推……
- 然後就有了動態轉移方程了f[i][j] = f[i-1][j] + f[i-1][j-1] + ... + f[i-1][j-i+1]
- 用j-1代換j 則: f[i][j-1] = f[i-1][j-1] + f[i-1][j-2] + ... + f[i-1][j-i]
- 用上面的式子減下面的式子 得:f[i][j] = f[i][j-1] + f[i-1][j] - f[i-1][j-i]
- 然後就可以碼程式碼啦
程式碼展示
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 105,mod = 1799999; int n,k,sum; ll f[maxn][maxn*maxn]; int main(){ scanf("%d%d",&n,&k); for(int i = 1;i <= n;++i)f[i][0] = 1; for(int i = 1;i <= n;++i){ sum += i - 1;//優化 前i個最多出現sum個逆序對 for(int j = 1;j <= k;++j){ if(j > sum)break; f[i][j] = (f[i][j-1] + f[i-1][j]) % mod; if(i <= j) f[i][j] = (f[i][j] - f[i-1][j-i] + mod) % mod;//防止相減出現負數 } } printf("%lld\n",f[n][k] % mod); return 0; }