cyand1317

分类

链接

RSS

RSS Link
大作死([UOJ 186] [UR #13] Yist)
NOI 2016 酱油记

[BZOJ 4517] [SDOI 2016] 排列计数

cyand1317 posted @ 2016年4月15日 22:55 in 未分类 with tags BZOJ Number theory Binomial inversion , 630 阅读

省选前最后水一发…

求有多少种长度为 $n$ 的序列 $A$,满足以下条件:
* $1 \sim n$ 这 $n$ 个数在序列中各出现了一次
* 若第 $i$ 个数 $A_i$ 的值为 $i$,则称 $i$ 是稳定的。序列恰好有 $m$ 个数是稳定的
满足条件的序列可能很多,序列数对 $10^9+7$ 取模。

SDOI 好像很喜欢数学题?_(:_」∠)_

用 $f(n)$ 表示 $1 \sim n$ 的错排方案数(满足 $A_i \neq i, \forall i$ 的排列数),容易看出 $Ans = f(n-m) \binom{n}{m}$。

其中有 $f(n) = \sum_{i=0}^{n} (-1)^i \binom{n}{i} (n-i)!$。具体证明可以 Orz vfk

接下来是喜闻乐见的化简时间

\[\begin{align}
Ans & = f(n-m) \binom{n}{m} \\
 & = \sum_{i=0}^{n-m} (-1)^i \binom{n-m}{i} (n-m-i)! \cdot \binom{n}{m} \\
 & = \sum_{i=0}^{n-m} (-1)^i \frac{(n-m)!}{i! (n-m-i)!} \cdot (n-m-i)! \cdot \frac{n!}{m! (n-m)!} \\
 & = \sum_{i=0}^{n-m} (-1)^i \frac{n!}{m! \cdot i!} \\
 & = \frac{n!}{m!} \cdot \sum_{i=0}^{n-m} \frac{(-1)^i}{i!}
\end{align}\]

惊奇地发现(一点也不)所有的阶乘以及除法逆元都可以 $O(n \log n)$ 预处理(不会 $O(n)$ _(:_」∠)_)。。然后又惊奇地发现 $\sum_{i=0}^{n-m} \frac{(-1)^i}{i!}$ 可以通过 $O(n)$ 预处理计算部分和。。于是变成了 $O(n \log n)-O(1)$ 目测 60s 不会被卡常数?= =

 

然后就愉快地解决了(难得不靠找规律做出一道数学题 QwQ)时间复杂度 $O(n \log n + t)$

/**************************************************************
    Problem: 4517
    User: cyand1317
    Language: C
    Result: Accepted
    Time:31724 ms
    Memory:24192 kb
****************************************************************/

#include <stdio.h>
#define MAXN 1000004
#define MODULUS 1000000007
typedef long long int64;
 
int n, m;
 
// f(n) = # of permutations P of length n
//        where not exists such i that P[i] = i
// F(n) = sigma[i = 0 to n] f(i) * C(n,i)
//      = n!
 
// Binomial inversion
// f(n) = sigma[i = 0 to n] (-1)^i * C(n,i) * (n-i)!
// Ans = f(n-m) * C(n,m)
//     = sigma[i = 0 to n-m] (-1)^i * C(n-m,i) * (n-m-i)! * C(n,m)
 
// C(n,m) * C(n-m,i) * (n-m-i)!
// = n!/(m! (n-m)!) * (n-m)!/(i! (n-m-i)!) * (n-m-i)!
// = n!/(m! i! (n-m-i)!) * (n-m-i)!
// = n!/(m! i!)
// Ans = (sigma[i = 0 to n-m] (-1)^i / i!) * n!/m!
 
int64 fact[MAXN], fact_inv[MAXN], pfxsum[MAXN];
int64 fpow(int64 base, int exp)
{
    int64 ans = 1;
    while (exp) {
        if (exp & 1) ans = ans * base % MODULUS;
        base = base * base % MODULUS;
        exp >>= 1;
    }
    return ans;
}
void preprocess()
{
    int i;
    fact[0] = fact[1] = 1;
    fact_inv[0] = fact_inv[1] = 1;
    pfxsum[0] = 1; pfxsum[1] = 0;
    for (i = 2; i < MAXN; ++i) {
        fact[i] = fact[i - 1] * i % MODULUS;
        fact_inv[i] = fpow(fact[i], MODULUS - 2);
        if (i & 1) pfxsum[i] = (pfxsum[i - 1] - fact_inv[i] + MODULUS) % MODULUS;
        else pfxsum[i] = (pfxsum[i - 1] + fact_inv[i]) % MODULUS;
    }
}
int64 nCr(int n, int m)
{
    return (fact[n] * fact_inv[m]) % MODULUS * fact_inv[n - m] % MODULUS;
}
int64 calc(int n, int m)
{
    return n < m ? 0 : pfxsum[n - m] * fact[n] % MODULUS * fact_inv[m] % MODULUS;
}
 
int main()
{
    preprocess();
    int t; scanf("%d", &t);
    do {
        scanf("%d%d", &n, &m);
        printf("%lld\n", calc(n, m));
    } while (--t);
    return 0;
}

貌似 $O(n \log n)$ 跑得挺慢的?= =

之前 WA 了一发。。原因是计算部分和的时候没有 mod MODULUS。。(果然还是弱 省选药丸药丸啊←_←)

总之省选加油  Ganbarimasu~


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter