【NOIP2013模拟联考6】选课(select)(组合计数+容斥)

【NOIP2013模拟联考6】选课(select)
  • 题意:

    • \(1\sim n\)的排列中,\(i\)不放在\(i\)\(i+1\)\(n\)不放在\(1\)的方案数.
    • \(n\le 100000\)
  • 首先令\(W(k)\)表示至少有\(k\)个数不合法的方案.

  • 这等价于在\(1,2,2,3,3,4,4,\cdots, n-1, n-1, n, n, 1\)这圆环中选取\(k\)个不相邻的数.

  • 首先考虑序列,方案数是\(\binom{2*n-k+1}{k}\),等价于在选出的\(k\)个数后添加对应的\(k-1\)个数,使其互不相邻.

  • 然后考虑环,可以再来一次正难则反,直接算出“反”的方案数,即首位相邻的方案数,是\(\binom{2*n-k-1}{k-2}\).

  • 然后直接容斥即可.

  • \[Answer\ =\sum_{i=0}^n W(i)*(n - i)!*(-1)^i\]

#include <cstdio>

#define ll long long
#define F(i, a, b) for (int i = a; i <= b; i ++)

const ll N = 2e5 + 10, mo = 1e9 + 7;

using namespace std;

ll jc[N], ny[N], F[N], n, s;

ll ksm(ll x, ll y) {
    for (s = 1; y ; y >>= 1, x = (x * x) % mo)
        if (y & 1) s = (s * x) % mo;
    return s;
}

int main() {
    jc[0] = ny[0] = 1;
    F(i, 1, N - 10) jc[i] = (jc[i - 1] * i) % mo, ny[i] = ksm(jc[i], mo - 2);
    
    while (scanf("%d", &n) != EOF) {
        if (n == 1) { printf("0\n"); continue; }
        s = (- 2 * n * jc[n - 1]) % mo;
        F(i, 2, n)
            s = (s + ((i & 1) ? - 1 : 1) * (((((2 * n * jc[2 * n - i - 1]) % mo) * ((ny[2 * n - 2 * i] * ny[i]) % mo) % mo)) % mo) * jc[n - i]) % mo;
        printf("%d\n", ((jc[n] + s) % mo + mo) % mo);
    }
}

猜你喜欢

转载自www.cnblogs.com/Pro-king/p/9383531.html