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

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

题意

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

思路

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

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

则对于任意一个n来说, $$f_n=\sum_{i=1}^{n-1}i!*[(n-i)!-f_{n-i}]$$

$$f_n=\sum_{i=1}^{n-1}i!*(n-i)!-\sum_{i=1}^{n-1}i!*f_{n-i}$$

设$g_n=n!$,特别的,$g_0=0!=0$,则:

$$f=g*g-g*f$$

$$f=\frac{g*g}{1+g}$$

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

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

Code(1579MS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
##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();
}

本文作者:jujimeizuo
本文地址https://blog.jujimeizuo.cn/2021/03/03/nowcoder-practice32-f/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!