2022牛客多校E.Falfa with Substring题解
题意
给定一个 \(n\),问长度为 \(n\) 的含有 bit
为连续子串的仅由小写字母构成的串个数
知识点
多项式,容斥
解法
我们可以推出两个东西来
\(f(i) = {n-2i \choose i} * 26^{n-3i}\)
\(g(i) = \sum_{j=i}^n (-1)^{j-i} * {j \choose i} * f(j)\)
其中,\(f(i)\) 表示有 \(i\) 个 bit
的长度为 \(n\) 的串数,带重复计数
\(g(i)\) 表示恰好有 \(i\) 个 bit
的长度为 \(n\) 的串数
\(g(i)\) 的式子可以用容斥得到
接下来对第二个式子下手,将其化成
\(i!*g(i)=\sum_{i=k}^{n}\frac{(-1)^{i-k}}{(i-k)!} * i!*f(i)\)
我们设
\([x^i]A(x)=i!*f(i)\)
\([x^i]B(x) = \frac{(-1)^{n-i}}{(n-i)!}\)
\([x^{n+i}]C(x)=i!*g(i)\)
于是得到 \([x^{n+k}]C(x)=\sum_{i=k}^{n} [x^i]A(x) * [x^{n-i}]B(x)\)
即 \(C = A * B\)
所以求出 \(A,B\) 然后NTT计算卷积即可
#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 3e6 + 10;
const int MOD = 998244353;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}
//常见模数
//65537 = 2^16+1,g = 3
//469762049, g = 3
//998244353 = 119 * 2^23 + 1, g = 3
//1004535809 = 479 * 2^21 + 1, g = 3
//4179340454199820289 = 29 * 2^57 + 1, g = 3
namespace NTT {
using poly = vector<LL>;
vector<int> R;
int lim = 0, L = 0;
const int MOD = 998244353, G = 3, Gi = 332748118;
//表示模数,原根,以及原根的逆元
inline LL q_pow(LL a, LL b, LL p) {
LL res = 1;
for (; b; b >>= 1) {
if (b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
void NTT(LL *A, int type) {
for (int i = 0; i < lim; i ++ ) if (i < R[i]) swap(A[i], A[R[i]]);
for (int mid = 1; mid < lim; mid <<= 1) {
LL wn = q_pow(type == 1 ? G : Gi, (MOD - 1) / (mid << 1), MOD);
for (int j = 0; j < lim; j += (mid << 1)) {
LL w = 1;
for (int k = 0; k < mid; k ++, w = w * wn % MOD) {
LL x = A[j + k], y = w * A[j + k + mid] % MOD;
A[j + k] = (x + y) % MOD;
A[j + k + mid] = (x - y + MOD) % MOD;
}
}
}
}
LL* multiply(int n, int m, LL *A, LL *B) {
//n, m分别是A,B的最大下标
lim = 1, L = 0;
while (lim <= n + m) lim <<= 1, L ++;
R.resize(lim, 0);
for (int i = 0; i < lim; i ++ ) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
NTT(A, 1), NTT(B, 1);
LL* ans = new LL[lim];
for (int i = 0; i < lim; i ++ ) ans[i] = A[i] * B[i] % MOD;
NTT(ans, -1);
LL inv = q_pow(lim, MOD - 2, MOD);
for (int i = 0; i <= n + m; i ++ ) ans[i] = ans[i] * inv % MOD;
return ans;
}
}
LL q_pow(LL a, LL b, LL p) {
LL res = 1;
for (; b; b >>= 1) {
if (b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
LL inv(LL x) {return q_pow(x, MOD - 2, MOD);}
LL f[N];
LL A[N], B[N];
inline void solve() {
int n; cin >> n;
f[0] = 1;
for (int i = 1; i <= n; i ++ ) f[i] = f[i - 1] * i % MOD;
for (int i = 0; i * 3 <= n; i ++ ) {
A[i] = f[n - 2 * i] * inv(f[n - 3 * i]) % MOD;
A[i] = A[i] * q_pow(26, n - 3 * i, MOD) % MOD;
}
for (int i = 0; i <= n; i ++ ) {
B[i] = inv(f[n - i]);
if ((n - i) & 1) B[i] = (-B[i] + MOD) % MOD;
}
LL *C = NTT::multiply(n, n, A, B);
for (int i = 0; i <= n; i ++ ) cout << C[i + n] * inv(f[i]) % MOD << ' ';
cout << endl;
}
signed main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
auto now = clock();
#endif
ios::sync_with_stdio(false), cin.tie(nullptr);
cout << fixed << setprecision(2);
// int T; cin >> T;
// while (T -- )
solve();
#ifdef DEBUG
cout << "============================" << endl;
cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
return 0;
}