计数难题3:LuoguT46780 妹子序列

Posted guessycb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计数难题3:LuoguT46780 妹子序列相关的知识,希望对你有一定的参考价值。

计数难题3:LuoguT46780 妹子序列

标签(空格分隔): 计数难题题选

题目大意:

丢个网址:戳我(QwQ)
给定(n)(m)
(n)的所有排列中,逆序对个数为(m)的排列个数。数据范围:(n,mleq 10^5)

题解

朴素(dp)(dp_{i,j})表示放完(i),有(j)个逆序对的方案数。
转移太简单了:(dp_{i,j} = sum_{k=0}^{i-1} dp_{i-1,k}) ,复杂度(O(n^2))美滋滋。
注意到放(i)时,我们的决策集合为(kin [0,i))
我们把每次的决策序列写出来,就一定能对应一个唯一的合法排列。
所以现在问题就变成了:满足决策的和为(m)的合法决策序列的个数。
构造生成函数:
[G(x) = prod_{i=1}^n [sum_{j=0}^{i-1} x^j]]

那么就是要求(x^m)项的系数,推式子:

[G(x) = prod_{i=1}^n[sum_{j=0}^{i-1}x^j] = prod_{i=1}^n frac{1-x^i}{1-x} = (frac{1}{1-x})^n prod_{i=1}^n (1-x^i)]

显然(frac{1}{1-x} = sum_{j=0}^{inf} x^j) ,考虑((frac{1}{1-x})^n) 的组合意义。
相当于有(n)种不同物品可以用,第(k)项的系数即从中选出(k)个可重物品的方案数。
即可重组合,所以有:
[S(x) = (frac{1}{1-x})^n = (sum_{j=0}^{inf}x^j)^n = sum_{j=0}^{inf} inom{j+n-1}{n-1}x^j]

对于后面的部分:(T(x) = prod_{i=1}^n(1-x^i)) ,同样考虑组合意义:
(n)个物品,第(i)个物品的体积为(i),第(k)项系数即用这些物品填满大小为(k)的背包的方案数。
古人云:背包计数 等价于 上升序列计数
这里每个物品只有一个,所以应该是一个严格上升序列
即我们要求:所有元素和为(k),且严格上升的合法序列个数,
(f_{i,j}) 表示放了(i)个物品,体积和为(j)的方案数,转移做有符号的序列计数。

  • 新增一个物品:(f_{i-1,j-i}) (即先给原来的(i-1)个物品+1,然后再添加一个1)
  • 把原来所有的物品加(1)(f_{i,j-i})
  • 由于物品体积不能超过(n),所以强制后减去不合法:(-f_{i-1,j-(n+1)})

综上所述,转移方程是(注意转移时带符号):(f_{i,j} = f_{i,j-i} - f_{i-1,j-i} - (-f_{i-1,j-(n+1)}))
而由于每种体积的物品只有一个,放置物品最多的方案应该是 (1+2+...+MAXleq m)
所以可以放置的物品数是(2sqrt m+2) 级别的,故只用做这么多次转移即可。
显然(T(x) = sum_{j=0}^{inf}[sum_{i=0}^{2sqrt m + 2} f_{i,j}]x^j) ,而我们有:
[G(x) = S(x) * T(x)]

所以卷积后即可得到第(m)项的系数,复杂度(O(nsqrt n))

实现代码

#include<bits/stdc++.h>
#define _ 100005
using namespace std ;

const int mod = 998244353 ; 
int dp[520][_],f[_],g[_],Fac[_<<1],IFac[_<<1],inv[_<<2],n,m,Ans ; 

int Comb(int N , int M) {
    if(M > N) return 0 ;
    return 1ll * Fac[N] * IFac[M] % mod * IFac[N - M] % mod ; 
}
int main() {
    freopen("testdata.in","r",stdin) ;
    cin >> n >> m ; 
    inv[0] = inv[1] = Fac[0] = Fac[1] = IFac[0] = IFac[1] = 1 ;
    for(int i = 2; i <= n + m ; i ++) {
        Fac[i] = 1ll * Fac[i-1] * i % mod ;
        inv[i] = 1ll * (mod-mod/i) * inv[mod%i] % mod ;
        IFac[i] = 1ll * IFac[i-1] * inv[i] % mod ; 
    }
    for(int i = 0; i <= m; i ++) f[i] = Comb(i + n - 1 , n - 1) ;
    dp[0][0] = 1 ;  g[0] = 1 ;
    int d = 1 ;
    for(int j = 2; j <= m; j ++) if(1ll * j * (j + 1) / 2 >= m) {d = j ; break ;}
    for(int i = 1; i <= d; i ++) {
        for(int j = 0; j <= m; j ++) {
            if(j>=i) dp[i][j] = (dp[i][j - i] - dp[i-1][j - i] + mod) % mod ;
            if(j>=(n+1))
                dp[i][j] = (dp[i][j] + dp[i - 1][j - (n + 1)]) % mod ; 
        }
        for(int j = 0; j <= m; j ++) g[j] = (g[j] + dp[i][j]) % mod ; 
    }
    Ans = 0 ;
    for(int i = 0; i <= m; i ++)
        Ans = (Ans + 1ll * f[i] * g[m - i] % mod) % mod ;
    cout << Ans << endl ;
    return 0 ; 
}

以上是关于计数难题3:LuoguT46780 妹子序列的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3809 Gty的二逼妹子序列

Bzoj 3809: Gty的二逼妹子序列 莫队,分块

BZOJ 3809 3809: Gty的二逼妹子序列 (莫队+分块)

bzoj3809:Gty的二逼妹子序列

BZOJ 3809 Gty的二逼妹子序列 | 莫队 + 分块

P4867 Gty的二逼妹子序列