牛客练习赛32 F-Friendly Polynomial NTT+多项式求逆+组合计数

牛客练习赛32 F-Friendly Polynomial NTT+多项式求逆+组合计数


传送门: https://ac.nowcoder.com/acm/contest/272/F

题意

给 一 个 n , 表 示 有 1 到 n 这 些 数 , 对 于 一 个 i , 如 果 前 i 个 是 1 到 i 的 一 个 排 列 , 则 整 个 序 列 称 为 不 合 格 序 列 。 给一个n,表示有1到n这些数,对于一个i,如果前i个是1到i的一个排列,则整个序列称为不合格序列。 n1nii1i
求 出 所 有 不 合 格 序 列 个 数 。 求出所有不合格序列个数。

思路

考 虑 把 序 列 以 i 为 界 限 拆 成 两 半 , 设 f n 为 长 度 为 n 的 不 合 格 序 列 个 数 , 则 : 考虑把序列以i为界限拆成两半,设f_n为长度为n 的不合格序列个数,则: ifnn

  • 前 i 个 是 不 合 格 序 列 , 贡 献 为 i ! 。 前i个是不合格序列,贡献为i!。 ii!
  • 后 n − i 个 不 是 不 合 格 序 列 , 贡 献 为 ( n − i ) ! − f n − i 后n-i个不是不合格序列,贡献为(n-i)!-f_{n-i} ni(ni)!fni

则 对 于 任 意 一 个 n 来 说 , 则对于任意一个n来说, n
f n = ∑ i = 1 n − 1 i ! ∗ [ ( n − i ) ! − f n − i ] f_n=\sum_{i=1}^{n-1}i!*[(n-i)!-f_{n-i}] fn=i=1n1i![(ni)!fni]

f n = ∑ i = 1 n − 1 i ! ∗ ( n − i ) ! − ∑ i = 1 n − 1 i ! ∗ f n − i f_n=\sum_{i=1}^{n-1}i!*(n-i)!-\sum_{i=1}^{n-1}i!*f_{n-i} fn=i=1n1i!(ni)!i=1n1i!fni

设 g n = n ! , 特 别 的 , g 0 = 0 ! = 0 , 则 : 设\red{g_n=n!,特别的,g_0=0!=0,则:} gn=n!g0=0!=0

f = g ∗ g − g ∗ f f=g*g-g*f f=gggf

f = g ∗ g 1 + g f=\frac{g*g}{1+g} f=1+ggg

需 要 一 个 多 项 式 求 逆 , 多 项 式 乘 法 , 直 接 套 即 可 。 需要一个多项式求逆,多项式乘法,直接套即可。

因 为 我 的 板 子 都 是 任 意 模 数 M T T , 所 以 直 接 省 略 N T T 了 , 全 是 M T T 。 因为我的板子都是任意模数MTT,所以直接省略NTT了,全是MTT。 MTTNTTMTT

Code(1579MS)

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

typedef long long ll;

#define endl "\n"

const ll mod = 998244353;
const double PI = acos(-1);


const int N = 1e6 + 10;

struct Complex {
    double x, y;
    Complex(double a = 0, double b = 0): x(a), y(b) {}
    Complex operator + (const Complex &rhs) { return Complex(x + rhs.x, y + rhs.y); }
    Complex operator - (const Complex &rhs) { return Complex(x - rhs.x, y - rhs.y); }
    Complex operator * (const Complex &rhs) { return Complex(x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x); }
    Complex conj() { return Complex(x, -y); }
} w[N];

int tr[N];
ll f[N], g[N], invg[N], g2[N];

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans % mod;
}

int getLen(int n) {
    int len = 1; while (len < (n << 1)) len <<= 1;
    for (int i = 0; i < len; i++) tr[i] = (tr[i >> 1] >> 1) | (i & 1 ? len >> 1 : 0);
    for (int i = 0; i < len; i++) w[i] = w[i] = Complex(cos(2 * PI * i / len), sin(2 * PI * i / len));
    return len;
}

void FFT(Complex *A, int len) {
    for (int i = 0; i < len; i++) if(i < tr[i]) swap(A[i], A[tr[i]]);
    for (int i = 2, lyc = len >> 1; i <= len; i <<= 1, lyc >>= 1)
        for (int j = 0; j < len; j += i) {
            Complex *l = A + j, *r = A + j + (i >> 1), *p = w;
            for (int k = 0; k < i >> 1; k++) {
                Complex tmp = *r * *p;
                *r = *l - tmp, *l = *l + tmp;
                ++l, ++r, p += lyc;
            }
        }
}

inline void MTT(ll *x, ll *y, ll *z, int n) {
    int len = 1; while (len <= n) len <<= 1;
    for (int i = 0; i < len; i++) tr[i] = (tr[i >> 1] >> 1) | (i & 1 ? len >> 1 : 0);
    for (int i = 0; i < len; i++) w[i] = w[i] = Complex(cos(2 * PI * i / len), sin(2 * PI * i / len));

    for (int i = 0; i < len; i++) (x[i] += mod) %= mod, (y[i] += mod) %= mod;
    static Complex a[N], b[N];
    static Complex dfta[N], dftb[N], dftc[N], dftd[N];

    for (int i = 0; i < len; i++) a[i] = Complex(x[i] & 32767, x[i] >> 15);
    for (int i = 0; i < len; i++) b[i] = Complex(y[i] & 32767, y[i] >> 15);
    FFT(a, len), FFT(b, len);
    for (int i = 0; i < len; i++) {
        int j = (len - i) & (len - 1);
        static Complex da, db, dc, dd;
        da = (a[i] + a[j].conj()) * Complex(0.5, 0);
        db = (a[i] - a[j].conj()) * Complex(0, -0.5);
        dc = (b[i] + b[j].conj()) * Complex(0.5, 0);
        dd = (b[i] - b[j].conj()) * Complex(0, -0.5);
        dfta[j] = da * dc;
        dftb[j] = da * dd;
        dftc[j] = db * dc;
        dftd[j] = db * dd;
    }
    for (int i = 0; i < len; i++) a[i] = dfta[i] + dftb[i] * Complex(0, 1);
    for (int i = 0; i < len; i++) b[i] = dftc[i] + dftd[i] * Complex(0, 1);
    FFT(a, len), FFT(b, len);
    for (int i = 0; i < len; i++) {
        int da = (ll)(a[i].x / len + 0.5) % mod;
        int db = (ll)(a[i].y / len + 0.5) % mod;
        int dc = (ll)(b[i].x / len + 0.5) % mod;
        int dd = (ll)(b[i].y / len + 0.5) % mod;
        z[i] = (da + ((ll)(db + dc) << 15) + ((ll)dd << 30)) % mod;
    }
}

void Get_Inv(ll *f, ll *g, int n) {
    if(n == 1) { g[0] = quick_pow(f[0], mod - 2); return ; }
    Get_Inv(f, g, (n + 1) >> 1);
    int len = getLen(n);
    static ll c[N];
    for(int i = 0;i < len; i++) c[i] = i < n ? f[i] : 0;
    MTT(c, g, c, len); MTT(c, g, c, len);
    for(int i = 0;i < n; i++) g[i] = (2ll * g[i] - c[i] + mod) % mod;
    for(int i = n;i < len; i++) g[i] = 0;
    for(int i = 0;i < len; i++) c[i] = 0;
}

void init() {
    g[0] = 1; for(int i = 1;i < 1e5 + 10; i++) g2[i] = g[i] = g[i - 1] * i % mod;
    Get_Inv(g, invg, 1e5);
    g[0] = g2[0] = 0;
    MTT(g2, g, g2, 1e5 + 1e5);
    MTT(g2, invg, f, 2e5 + 1e5);
}


void solve() {
    init();
    int _; cin >> _;
    while(_--) {
        int n; cin >> n;
        cout << f[n] << endl;
    }
}


signed main() {(cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值