AtCoder Beginner Contest 172 E - NEQ(二项式反演)

AtCoder Beginner Contest 172 E - NEQ

题意:

求满足下列条件的长度为 N N N且包含 [ 1 , M ] [1, M] [1,M]范围内整数的序列 A 1 , A 2 , ⋯   , A N A_1, A_2, \cdots, A_N A1,A2,,AN B 1 , B 2 , ⋯   , B N B_1, B_2, \cdots, B_N B1,B2,,BN组成的序列对数量。

  • ∀ 1 ≤ i < j ≤ N ,    A i ≠ A j ∧ B i ≠ B j \forall 1 \le i \lt j \le N, \; A_i \neq A_j \land B_i \neq B_j 1i<jN,Ai=AjBi=Bj
  • ∀ 1 ≤ i ≤ N ,    A i ≠ B i \forall 1 \le i \le N, \; A_i \neq B_i 1iN,Ai=Bi


分析:

条件① —— 保证序列A, B内所有数唯一 很好满足,接下来看条件② —— 序列A, B的对应位置的数不同

显然要让对应位置的数不同情况会很复杂,但至多有 K K K个位置的数不同的情况却很容易求解,因为这等价于有 至少 N − K N-K NK个位置的数相同

设:
f ( K ) f(K) f(K):序列内各数唯一且至多有 K K K个对应位置的数不同(至少 N − K N-K NK个对应位置的数相同)的情况数;
g ( K ) g(K) g(K):序列内各数唯一且恰有 K K K个特定的对应位置的数不同的情况数;

易得:
f ( N ) = ∑ K = 0 N ( N K ) g ( K ) f(N) =\sum_{K=0}^N \binom{N}{K}g(K) f(N)=K=0N(KN)g(K)根据二项式反演可得:
g ( N ) = ∑ K = 0 N ( − 1 ) N − K ( N K ) f ( K ) g(N) = \sum_{K=0}^N (-1)^{N-K} \binom{N}{K} f(K) g(N)=K=0N(1)NK(KN)f(K)答案即为 g ( N ) 。 g(N)。 g(N)

根据至少 N − K N-K NK个对应位置的数相同,可知 f ( K ) = P M N − K × ( P M − N + K K ) 2 f(K) = P_M^{N-K} \times (P_{M-N+K}^K)^2 f(K)=PMNK×(PMN+KK)2

最终答案如下:
g ( N ) = ∑ K = 0 N ( − 1 ) N − K ( N K ) P M N − K × ( P M − N + K K ) 2 g(N) = \sum_{K=0}^N (-1)^{N-K} \binom{N}{K} P_M^{N-K} \times (P_{M-N+K}^K)^2 g(N)=K=0N(1)NK(KN)PMNK×(PMN+KK)2



代码:

#include 
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 5e5 + 10;
int N, M;
LL fact[maxn], fact_inv[maxn];
LL qpow(LL a, LL b)
{
    LL res = 1;
    a %= MOD;
    while(b)
    {
        if(b & 1)
            res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
void preprocess()
{
    fact[0] = 1;
    for(int i = 1; i <= M; i++)
        fact[i] = fact[i-1] * i % MOD;
    fact_inv[M] = qpow(fact[M], MOD - 2);
    for(int i = M; i >= 1; i--)
        fact_inv[i-1] = fact_inv[i] * i % MOD;
}
LL comb(int a, int b)
{
    if(a < 0 || b < 0 || a < b)
        return 0;
    else
        return fact[a] * fact_inv[b] % MOD * fact_inv[a-b] % MOD;
}
LL perm(int a, int b)
{
    if(a < 0 || b < 0 || a < b)
        return 0;
    else
        return fact[a] * fact_inv[a-b] % MOD;
}
int main()
{
    scanf("%d %d", &N, &M);
    preprocess();
    LL ans = 0;
    for(int K = 0; K <= N; K++)
        ans = (ans +  qpow(-1, N - K) * comb(N, K) % MOD * perm(M, N - K) % MOD * qpow(perm(M - N + K, K), 2) % MOD) % MOD;
    printf("%lld\n", (ans % MOD + MOD) % MOD);
    return 0;
}

你可能感兴趣的:(★水题之路,★数学)