「2017 山東一輪集訓 Day7」逆序對(生成函數)(容斥)(DP)

傳送門

題意:長度爲 nn,有 kk 個逆序對的排列個數?
n,k1e5n,k\le 1e5

考慮新填一個數 ii 進去,可能產生的貢獻爲 [0,i1][0,i-1],於是問題等價於:
i=1nai=k,ai[0,i1]\sum_{i=1}^na_i=k,a_i\in[0,i-1] 的方案數
其實可以大力生成函數
F(x)=1(1+x)(1+x+x2)...(1+x+x2+...+xn)F(x)=1(1+x)(1+x+x^2)...(1+x+x^2+...+x^n)
=i=1n(1xi)(1x)n=\frac{\prod_{i=1}^n(1-x^i)}{(1-x)^n}
=(i=1n(1xi))(j=0n(j+n1j)xj)=(\prod_{i=1}^n(1-x^i))(\sum_{j=0}^n\binom{j+n-1}{j}x^j)
考慮枚舉後面的,需要快速知道前面的第 i[0,k]i\in[0,k]
考慮其組合意義,就是選若干個不重複的數,和爲 ii,貢獻爲 (1)(-1)^{個數}
這裏的個數顯然是 k\sqrt k 級別的,於是可以 dpdp
fi,jf_{i,j} 表示 ii 個,和爲 jj 的方案,發現就是整體加若干個 1 填一個 1 再加整體加若干個 1
有一個坑點是當 j>nj>n 時,可能有一個數被加成 n+1n+1,強制填 n+1n+1 容斥就可以了
複雜度 O(kk)O(k\sqrt k)

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int M = 1000, N = 1e5 + 50;
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
int sgn(int a){ return (a&1)?Mod-1:1; }
void Add(int &a, int b){ a = add(a, b); }
int n, m, k, f[M][N];
int fac[N<<1], ifac[N<<1];
int C(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fac[n],mul(ifac[m],ifac[n-m])); }
void prework(int n){
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i);
	ifac[n] = ksm(fac[n], Mod-2);
	for(int i = n-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
}
int calc(int k){
	int ans = 0;
	for(int i = 0; i <= m; i++) Add(ans, mul(sgn(i),f[i][k]));
	return ans;
}
int main(){
	cin >> n >> k; prework(n + k + 5);
	m = sqrt(k * 2) + 5; f[0][0] = 1;
	for(int i = 1; i <= m; i++)
	for(int j = i; j <= k; j++){
		if(j >= i) f[i][j] = add(f[i][j-i], f[i-1][j-i]);
		if(j > n) f[i][j] = dec(f[i][j], f[i-1][j-n-1]);
	}
	int ans = 0;
	for(int i = 0; i <= k; i++){
		Add(ans, mul(C(i+n-1,i),calc(k-i)));
	} cout << ans; return 0;
}
發佈了651 篇原創文章 · 獲贊 98 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章