05.15 模拟赛T2题解

#6077. 「2017 山东一轮集训 Day7」逆序对


第一个数可以贡献的的逆序对为0

第二个数可以贡献的的逆序对为0,1

第三个数可以贡献的的逆序对为0,1,2
....

显然答案的生成函数

\[=\prod\limits_{i=1}^n \sum\limits_{j=0}^{i-1}x^j \]

\[=\prod\limits_{i=1}^n\frac{1-x^i}{1-x} \]

\[=(\sum_{i=0}^\infin x^i)^n \cdot \prod\limits_{i=1}^n 1-x^i \]

我们要求其第k项。

前一半显然二项式定理打开,后一半有两种处理方法。

  • 1.多项式Ln+Exp.这不是我们的重点。(理论复杂度小,但是常数大,代码难写,不再展开。)

  • 2.巧妙的DP。后面那个东西有组合意义

\({1,2,3,...n}\)中的\(i\)个数,其和为\(k\),贡献为\((-1)^i\)

如果我们设\(f_{i,j}\)表示用\(i\)个数凑出和为\(j\)的方案数,由于 “体积” 是连续整数,考虑

  • 每个数+1 \(f_{i,j} \leftarrow f_{i,j-i}*1\)

  • 每个数+1,再补一个1 \(f_{i,j} \leftarrow f_{i-1,j-i}*1\)

  • 去掉不小心选到\(n+1\)的情况,\(f_{i,j} \leftarrow f_{i-1,j-n-1}*(-1)\)

最后乘个容斥系数。

第一维是\(O(n^{0.5})\)的,所以总复杂度\(O(n^{1.5})\).

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

const int MOD = 1e9 + 7 , N = 100010 , T = 480;
int n , k , fac[N * 2] , ifac[N * 2] , iv[N * 2] , ans = 0;

inline int read()
{
    int x = 0 ; char c = getchar(); bool f = 0;
    while(c < '0' || c > '9') f |= (c=='-') , c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + (c^48) , c = getchar();
    return f ? -x : x;
}

inline int qpow(int x , int y = MOD - 2) {
	int res = 1;
	for(; y ; y >>= 1 , x = 1ll * x * x % MOD)
		if(y & 1) res = 1ll * res * x % MOD;
	return res; 
} 

int C(int x , int y)
{
	if(y < 0 || y > x) return 0;
	return 1ll * fac[x] * ifac[y] % MOD * ifac[x - y] % MOD; 
}
typedef long long int64;

void C_init(int n) {
    iv[1] = 1;
    for(int i = 2 ; i <= n ; i++) iv[i] = 1ll * (MOD - MOD/i) * iv[MOD % i] % MOD;

    ifac[0] = fac[0] = 1;
    for(int i = 1 ; i <= n ; i++) 
        fac[i] = 1ll * fac[i - 1] * i % MOD ,
        ifac[i] = 1ll * ifac[i - 1] * iv[i] % MOD;
}
int dp[T][N];
int main() {
	freopen("b.in" , "r" , stdin);
	freopen("b.out" , "w" , stdout); 
    n = read() , k = read();
    C_init(n + k);

    dp[0][0] = 1;
    for(int i = 1 ; i*(i+1)/2 <= k ; ++i)
        for(int j = i ; j <= k ; ++j) {
            dp[i][j] = ((long long)dp[i][j] + dp[i][j - i] + dp[i - 1][j - i]) % MOD;
            if(j > n) dp[i][j] = (dp[i][j] + MOD - dp[i - 1][j - 1 - n]) % MOD;
        }

    for(int i = 0 ; i <= k ; ++i) {
        int res = 0;
        for(int j = 0 ; j*(j+1)/2 <= i ; ++j) {
            if(j & 1) res = (res - dp[j][i] + MOD) % MOD;
            else res = (res + dp[j][i]) % MOD;
        }
        ans = (ans + 1ll * res * C(n + (k - i) - 1 , n - 1)) % MOD;
    }

    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/icantfindaname/p/12934260.html