[2017 山東一輪集訓 Day7] 逆序對
阿新 • • 發佈:2021-08-09
一、題目
這麼簡單的去重我竟然沒想到,我是個哈批。
二、解法
首先有一個顯然的 \(dp\),依次加入 \(1\) 到 \(i\),每次考慮逆序對的增量:
\[dp[i][j+k]\leftarrow dp[i-1][j] \ \ \ k\in[0,i) \]這個可以用字首和優化,時間複雜度 \(O(n^2)\),可以寫成生成函式的形式:
\[\prod_{i=1}^n\sum_{j=0}^ix^j=\prod_{i=1}^n\frac{1-x^{i}}{1-x} \]分母並不需要多項式求逆,可以直接最後隔板法組合意義算,問題是 \(\prod 1-x^i\) 的計算,這個算式從容斥的角度也可以解釋,也就是欽定一個位置的逆序對不合法就會帶來 \(-1\)
因為我多項式學得太差了所以只會 \(dp\),考慮欽定位置的個數要 \(\leq\sqrt {2k}\) 才有可能有方案,發現這個東西就是柱狀圖 \(dp\) 板子,考慮有若干個柱子,每次可以新增一個柱子或者把所有柱子增加 \(1\) 的高度,那麼轉移,設 \(dp[i][j]\) 表示有 \(i\) 個柱子,柱子的總高度是 \(j\),我們從小到大列舉 \(j\):
\[dp[i][j]\leftarrow dp[i-1][j-i]+dp[i][j-i] \]但是會算重,因為如果出現高度為 \(n+1\) 的柱子就不合法,那麼直接減去這種方案即可:
\[dp[i][j]\leftarrow dp[i][j]-dp[i-1][j-n-1] \]設 \(f(t)\)
時間複雜度 \(O(n\sqrt n)\)
#include <cstdio> #include <cmath> const int M = 100005; const int MOD = 1e9+7; #define int long long int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,k,dp[500][M],fac[2*M],inv[2*M],ans; void init(int n) { fac[0]=inv[0]=inv[1]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD; for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD; } int C(int n,int m) { if(n<m || m<0) return 0; return fac[n]*inv[m]%MOD*inv[n-m]%MOD; } int cal(int x) { return C(x+n-1,n-1); } signed main() { //freopen("perm.in","r",stdin); //freopen("perm.out","w",stdout); n=read();k=read();init(2e5); dp[0][0]=1;m=499; for(int i=1;i<=m;i++) { for(int j=i;j<=k;j++) { dp[i][j]=(dp[i-1][j-i]+dp[i][j-i])%MOD; if(j>=n+1) dp[i][j]=(dp[i][j]-dp[i-1][j-n-1])%MOD; } } for(int i=0;i<=m;i++) for(int j=0;j<=k;j++) { int f=(i%2?-1:1); ans=(ans+1ll*f*dp[i][k-j]*cal(j))%MOD; } printf("%lld\n",(ans+MOD)%MOD); }