CF960G Bandit Blues

第300篇blog yeah


https://www.luogu.com.cn/problem/CF960G

这题是P4609 [FJOI2016]建筑师的加强版
一眼秒了,没有什么好说的

考虑 n n n会把序列分成左右两个部分
对于每个建筑,把它和被它挡住的放在一个集合,假设这个集合大小为sz,则方案数为 ( s z − 1 ) ! (sz-1)! (sz1)!除了最高那个在最前面,后面的可以随便交换位置,容易发现这就是圆排列,进而得出第一类斯特林数
那么答案显然就是 [ n − 1 A − 1 + B − 1 ] × ( A − 1 + B − 1 A − 1 ) \begin{bmatrix}n-1 \\ A-1+B-1\end{bmatrix} \times \binom{A-1+B-1}{A-1} [n1A1+B1]×(A1A1+B1)
第一类斯特林数求一项并不好求,所以可以直接大力把一行求出来,取对应项即可
code:

#include<bits/stdc++.h>
#define mod 998244353
#define N 4000500
using namespace std;
int add(int x, int y) { x += y;
    if(x >= mod) x -= mod;
    return x;
}
int sub(int x, int y) { x -= y;
    if(x < 0) x += mod;
    return x;
}
int mul(int x, int y) {
    return 1ll * x * y % mod;
}
int qpow(int x, int y) {
    int ret = 1;
    for(; y; y >>= 1, x = mul(x, x)) if(y & 1) ret = mul(ret, x);
    return ret;
}
const int G = 3;
const int Ginv = qpow(G, mod - 2);
int rev[N];
void ntt(int *a, int n, int o) {
    for(int i = 1; i < n; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (n >> 1));
    for(int i = 1; i < n; i ++) if(i > rev[i]) swap(a[i], a[rev[i]]);
    for(int len = 2; len <= n; len <<= 1) {
        int w0 = qpow(o == 1? G : Ginv, (mod - 1) / len);
        for(int j = 0; j < n; j += len) {
            int wn = 1;
            for(int k = j; k < j + (len >> 1); k ++, wn = mul(wn, w0)) {
                int X = a[k], Y = mul(a[k + (len >> 1)], wn);
                a[k] = add(X, Y); a[k + (len >> 1)] = sub(X, Y);
            }
        }
    }
    int ninv = qpow(n, mod - 2);
    if(o == -1)
        for(int i = 0; i < n; i ++) a[i] = mul(a[i], ninv);
}
int n, fac[N], ifac[N], a[N], b[N], f[N], g[N];
void init(int n) {
    fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = mul(fac[i - 1], i);
    ifac[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i --) ifac[i] = mul(ifac[i + 1], i + 1);
}
void Mul(int *a, int *b, int n, int m) {
    int len = 1;
    for(; len <= n + m; ) len <<= 1;
    ntt(a, len, 1), ntt(b, len, 1);
    for(int i = 0; i < len; i ++) a[i] = mul(a[i], b[i]);
    ntt(a, len, - 1);
    for(int i = n + m + 1; i <= len; i ++) a[i] = 0;
}
void solve(int *f, int n) {
    if(n == 0) {f[0] = 1; return ;}
    if(n == 1) {f[1] = 1; return ;}
    if(n & 1) {
        solve(f, n - 1);
        for(int i = n; i >= 1; i --) {
            f[i] = add(f[i - 1], mul(f[i], n - 1));
        }
        f[0] = mul(f[0], n - 1);
    } else {
        solve(f, n >> 1);
        int m = n / 2, mi = 1;
        for(int i = 0; i <= m; i ++, mi = mul(mi, m))
            a[i] = mul(f[i], fac[i]), b[i] = mul(mi, ifac[i]);
        reverse(a, a + 1 + m);
        Mul(a, b, m, m);
        for(int i = 0; i <= m; i ++) g[i] = mul(a[m - i], ifac[i]);
        Mul(f, g, m, m);
        int len = 1;
        for( ;len <= m + m; ) len <<= 1;
        for(int i = 0; i <= len; i ++) g[i] = a[i] = b[i] = 0;
     //   for(int i = 0; i <= n; i ++) printf("%d ", f[i]); printf("    %d\n", n);
    }
}
int C(int n, int m) {
    return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int A, B;
int main() {
    scanf("%d%d%d", &n, &A, &B);
    init(n);
    n --;
    solve(f, n);
    printf("%lld", 1ll * f[A + B - 2] * C(A + B - 2, A - 1) % mod);
    return 0;
}
posted @ 2021-08-23 11:42  lahlah  阅读(34)  评论(0编辑  收藏  举报