題解 「2017 山東一輪集訓 Day7」逆序對
Description
給定 $ n, k $,請求出長度為 $ n $ 的逆序對數恰好為 $ k $ 的排列的個數。答案對 $ 10 ^ 9 + 7 $ 取模。
對於一個長度為 $ n $ 的排列 $ p $,其逆序對數即滿足 $ i < j $ 且 $ p_i > p_j $ 的二元組 $ (i, j) $ 的數量。
一行兩個整數 $ n, k $。
一行,表示答案。
對於 $ 20% $ 的資料,$ n, k \leq 20 $;
對於 $ 40% $ 的資料,$ n, k \leq 100 $;
對於 $ 60% $ 的資料,$ n, k \leq 5000 $;
對於 $ 100% $ 的資料,$ 1 \leq n, k \leq 100000, 1 \leq k \leq \binom{n}{2} $。
Solution
可以想到,對於一個排列 \(p\) ,假設 \(s_i\) 表示以 \(i\) 為右端點的逆序對個數,那麼可以看出一個 \(s_{1,2,...,n}\) 對應一個唯一的 \(p\) ,而一個 \(s_{1,2,...,n}\) 合法當且僅當 \(\forall i,s_i\le i-1\)。
可以想到我們可以容斥,即列舉哪些點 \(s_i\) 越界了。那麼,我們也就只需要求出
\[\prod_{i=1}^{n}(1-x^i) \]的前面 \(k\) 項。
這個時候我們就有兩種辦法,一種是用多項式,取 \(\ln\) ,然後用 \(\ln(1-x)=\sum_{j=1}^{\infty} -\frac{x^j}{j}\)
還有另外一種 \(\Theta(k\sqrt k)\) 做法。你發現選出一個 \(\{1,2,...,n\}\) 的集合還有另外一種選法,即假設你現在有一個遞減序列,你每次有兩種選擇:
-
整體加 \(1\)
-
整體加 \(1\) 並在後面增加一個 \(1\)
那麼,我們就可以進行 dp 了,因為考慮到我們最多使用 \(\sqrt k\) 次操作 \(2\),所以,我們可以設 \(f_{i,j}\) 表示在經過 \(i\)
可以得到轉移式:
\[f_{i,j}=f_{i,j-i}+f_{i-1,j-i}-f_{i-1,j-n-1} \]Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
int n,k,upp = 2e5,fac[MAXN],ifac[MAXN],f[505][MAXN];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
void Sub (int &a,int b){a = dec (a,b);}
void Add (int &a,int b){a = add (a,b);}
int binom (int a,int b){return a >= b ? mul (fac[a],mul (ifac[b],ifac[a - b])) : 0;}
int F[MAXN];
signed main(){
read (n,k);int up = 500;
fac[0] = 1;for (Int i = 1;i <= upp;++ i) fac[i] = mul (fac[i - 1],i);
ifac[upp] = qkpow (fac[upp],mod - 2);for (Int i = upp;i;-- i) ifac[i - 1] = mul (ifac[i],i);
f[0][0] = 1;
for (Int i = 1;i <= up;++ i)
for (Int j = 0;j <= k;++ j){
if (j >= i) Add (f[i][j],add (f[i][j - i],f[i - 1][j - i]));
if (j >= n + 1) Sub (f[i][j],f[i - 1][j - n - 1]);
}
for (Int S = 0;S <= k;++ S)
for (Int i = 0;i <= up;++ i)
if (i & 1) Sub (F[S],f[i][S]);
else Add (F[S],f[i][S]);
int ans = 0;
for (Int S = 0;S <= k;++ S) Add (ans,mul (F[S],binom (k - S + n - 1,n - 1)));
write (ans),putchar ('\n');
return 0;
}