多项式例题2
CF1096G Lucky Tickets
All bus tickets in Berland have their numbers. A number consists of $ n $ digits ( $ n $ is even). Only $ k $ decimal digits $ d_1, d_2, \dots, d_k $ can be used to form ticket numbers. If $ 0 $ is among these digits, then numbers may have leading zeroes. For example, if $ n = 4 $ and only digits $ 0 $ and $ 4 $ can be used, then $ 0000 $ , $ 4004 $ , $ 4440 $ are valid ticket numbers, and $ 0002 $ , $ 00 $ , $ 44443 $ are not.
A ticket is lucky if the sum of first $ n / 2 $ digits is equal to the sum of remaining $ n / 2 $ digits.
Calculate the number of different lucky tickets in Berland. Since the answer may be big, print it modulo $ 998244353 $ .
Input
The first line contains two integers $ n $ and $ k $ $ (2 \le n \le 2 \cdot 10^5, 1 \le k \le 10) $ — the number of digits in each ticket number, and the number of different decimal digits that may be used. $ n $ is even.
The second line contains a sequence of pairwise distinct integers $ d_1, d_2, \dots, d_k $ $ (0 \le d_i \le 9) $ — the digits that may be used in ticket numbers. The digits are given in arbitrary order.
Output
Print the number of lucky ticket numbers, taken modulo $ 998244353 $ .
Solution 1
不难发现,定义 \(F(x) = \sum\limits_{i = 0}^9a_ix^i\),\(a_i\) 为 \(i\) 出现的次数,我们只需要求出 \(\sum\limits_{i = 0}^{5n}\left([x^i]F^{\frac{n}{2}}(x)\right)^2\),直接套板子,使用多项式快速幂,\(O(nk\log{nk})\) 复杂度下,小常数可以通过。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 1.5e6 + 10, _G = 3, mod = 998244353;
ll qpow(ll a, ll k = mod - 2) {
ll res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
const int invG = qpow(_G);
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void NTT(int *g, int op, int n) {
rev_init(n);
static ull f[N << 1], Gk[N << 1] = {1};
for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
for (int k = 1; k < n; k <<= 1) {
int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
for (int i = 0; i < n; i += k << 1) {
for (int j = 0; j < k; j ++ ) {
int tmp = Gk[j] * f[i | j | k] % mod;
f[i | j | k] = f[i | j] + mod - tmp;
f[i | j] += tmp;
}
}
if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
}
if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
else {
int invn = qpow(n);
for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
}
}
void px(int *f, int *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}
int inv[N << 1], inv_len;
void inv_init(int n) {
if (n <= inv_len) return;
if (!inv_len) inv[0] = inv[1] = 1, inv_len = 1;
for (int i = inv_len + 1; i <= n; i ++ ) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
inv_len = n;
}
void Poly_d(int *f, int n) {
for (int i = 1; i < n; i ++ ) f[i - 1] = 1ll * f[i] * i % mod;
f[n - 1] = 0;
}
void Poly_int(int *f, int n) {
for (int i = n; i; i -- ) f[i] = 1ll * f[i - 1] * inv[i] % mod;
f[0] = 0;
}
void covolution(int *f, int *g, int len, int lim) {
static int sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
NTT(sav, 1, n); NTT(f, 1, n);
px(f, sav, n); NTT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
void Poly_inv(int *f, int m) {
static int g1[N << 1], g2[N << 1], sav[N << 1];
int n; for (n = 1; n < m; n <<= 1);
g1[0] = qpow(f[0]);
for (int len = 2; len <= n; len <<= 1) {
cpy(g2, g1, len >> 1), cpy(sav, f, len);
NTT(g2, 1, len), NTT(sav, 1, len);
px(g2, sav, len); NTT(g2, -1, len);
clr(g2, len >> 1); cpy(sav, g1, len);
NTT(sav, 1, len); NTT(g2, 1, len);
px(g2, sav, len); NTT(g2, -1, len);
for (int i = len >> 1; i < len; i ++ ) g1[i] = (2ll * g1[i] - g2[i] + mod) % mod;
}
cpy(f, g1, m), clr(g1, n), clr(g2, n), clr(sav, n);
}
void Poly_ln(int *f, int n) {
static int sav[N << 1];
inv_init(n); cpy(sav, f, n);
Poly_d(sav, n); Poly_inv(f, n);
covolution(f, sav, n, n); Poly_int(f, n - 1);
clr(sav, n);
}
void Poly_exp(int *f, int m) {
static int b1[N << 1], b2[N << 1];
int n; for (n = 1; n < m; n <<= 1);
b1[0] = 1;
for (int len = 2; len <= n; len <<= 1) {
cpy(b2, b1, len >> 1); Poly_ln(b2, len);
for (int i = 0; i < len; i ++ ) b2[i] = (f[i] - b2[i] + mod) % mod;
b2[0] = (b2[0] + 1) % mod;
covolution(b1, b2, len, len);
}
cpy(f, b1, m); clr(b1, n); clr(b2, n);
}
void Poly_pow(int *f, int n, string k) {
int k1 = 0, k2 = 0, p = 0, c;
while (!f[p]) p ++ ;
for (int i = 0; k[i]; i ++ ) {
k1 = (10ll * k1 + k[i] - '0') % mod;
k2 = (10ll * k2 + k[i] - '0') % (mod - 1);
if (1ll * k1 * p >= n) return clr(f, n), void();
}
n -= p * k1; c = qpow(f[p]);
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i + p] * c % mod;
clr(f + n, p * k1); Poly_ln(f, n);
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * k1 % mod;
Poly_exp(f, n); c = qpow(c, mod - 1 - k2);
for (int i = n - 1; i >= 0; i -- ) f[p * k1 + i] = 1ll * f[i] * c % mod;
clr(f, p * k1);
}
int n, k, a[10];
int F[N << 1];
void solve() {
cin >> n >> k;
for (int i = 0; i < k; i ++ ) cin >> a[i], F[a[i]] ++ ;
Poly_pow(F, n * 5, to_string(n >> 1));
ll ans = 0;
for (int i = 0; i < n * 5; i ++ ) (ans += 1ll * F[i] * F[i] % mod) %= mod;
cout << ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
Solution 2
对低度多项式求幂,即
如此,我们得到了 \(G\) 的线性递推表达式,考虑已知前 \(i\) 次多项式系数,考虑求 \(x^{i + 1}\) 的系数,有
也就是
容易得到
而 \(F_0 = 1\) 确定,于是我们可以通过线性递推在 \(O(nk^2)\) 复杂度解决问题。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 2e6 + 10, _G = 3, mod = 998244353;
int n, k, a[10];
ll F[N], G[N], inv[N];
void solve() {
cin >> n >> k;
for (int i = 0; i < k; i ++ ) cin >> a[i];
int minn = *min_element(a, a + k), maxx = *max_element(a, a + k) - minn;
for (int i = 0; i < k; i ++ ) F[a[i] - minn] = 1;
n >>= 1; inv[1] = G[0] = 1;
for (int i = 2; i < N; i ++ ) inv[i] = mod - mod / i * inv[mod % i] % mod;
ll ans = 0;
for (int i = 0; i <= n * (maxx + 1); i ++ ) {
ll res = 0;
for (int j = 0; j <= min(i, maxx - 1); j ++ ) res += G[i - j] * F[j + 1] * (j + 1) % mod;
res = res % mod * n % mod;
for (int j = 1; j <= min(i, maxx); j ++ ) res -= F[j] * (i - j + 1) * G[i - j + 1] % mod;
G[i + 1] = res % mod * inv[i + 1] % mod;
ans += G[i] * G[i] % mod;
}
cout << ans % mod;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
CF632E Thief in a Shop
A thief made his way to a shop.
As usual he has his lucky knapsack with him. The knapsack can contain $ k $ objects. There are $ n $ kinds of products in the shop and an infinite number of products of each kind. The cost of one product of kind $ i $ is $ a_{i} $ .
The thief is greedy, so he will take exactly $ k $ products (it's possible for some kinds to take several products of that kind).
Find all the possible total costs of products the thief can nick into his knapsack.
Input
The first line contains two integers $ n $ and $ k $ ( $ 1<=n,k<=1000 $ ) — the number of kinds of products and the number of products the thief will take.
The second line contains $ n $ integers $ a_{i} $ ( $ 1<=a_{i}<=1000 $ ) — the costs of products for kinds from $ 1 $ to $ n $ .
Output
Print the only line with all the possible total costs of stolen products, separated by a space. The numbers should be printed in the ascending order.
根据上一题的经验,我们知道只用做 \(n\) 次卷积即可,这个题用多项式 exp+ln 固然可以,但是时限很紧,我们采用倍增快速幂的方式,根据主定理 \(T(n) = T(\frac{n}{2}) + O(n\log{n}) = O(n\log{n})\),所以总体时间复杂度是 \(O(n^2\log{n^2})\),同样,这个题卡模数,我们可以通过随机赋值,根据Schwartz–Zippel引理,这样做错误的概率是 \(\frac{d}{mod}\),可以通过。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 1.1e6 + 10, _G = 3, mod = 998244353;
ll qpow(ll a, ll k = mod - 2) {
ll res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
const int invG = qpow(_G);
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void NTT(int *g, int op, int n) {
rev_init(n);
static ull f[N << 1], Gk[N << 1] = {1};
for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
for (int k = 1; k < n; k <<= 1) {
int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
for (int i = 0; i < n; i += k << 1) {
for (int j = 0; j < k; j ++ ) {
int tmp = Gk[j] * f[i | j | k] % mod;
f[i | j | k] = f[i | j] + mod - tmp;
f[i | j] += tmp;
}
}
if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
}
if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
else {
int invn = qpow(n);
for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
}
}
void px(int *f, int *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}
void covolution(int *f, int *g, int len, int lim) {
static int sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
NTT(sav, 1, n); NTT(f, 1, n);
px(f, sav, n); NTT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
void Poly_pow(vector<int> &a, int k) {
static int b1[N << 1], b2[N << 1];
vector<int> ans = {1};
while (k) {
int len1 = ans.size(), len2 = a.size();
if (k & 1) {
for (int i = 0; i < len1; i ++ ) b1[i] = ans[i];
for (int i = 0; i < len2; i ++ ) b2[i] = a[i];
covolution(b1, b2, len1 + len2 >> 1, len1 + len2 - 1);
ans.resize(len1 + len2 - 1);
for (int i = 0; i < len1 + len2 - 1; i ++ ) ans[i] = b1[i];
clr(b1, len1 + len2 - 1); clr(b2, len2);
}
for (int i = 0; i < len2; i ++ ) b1[i] = b2[i] = a[i];
covolution(b1, b2, len2, (len2 << 1) - 1);
a.resize((len2 << 1) - 1);
for (int i = 0; i < (len2 << 1) - 1; i ++ ) a[i] = b1[i];
clr(b1, (len2 << 1) - 1); clr(b2, len2);
k >>= 1;
}
a = ans;
}
int n, k, a[N];
vector<int> F;
void solve() {
cin >> n >> k;
for (int i = 0; i < n; i ++ ) cin >> a[i];
int minn = *min_element(a, a + n), maxx = *max_element(a, a + n) - minn;
F.resize(maxx + 1);
for (int i = 0; i < n; i ++ ) F[a[i] - minn] = rand();
Poly_pow(F, k);
for (int i = 0; i <= maxx * k; i ++ ) if (F[i]) cout << i + minn * k << " ";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
CF958F3 Lightsabers (hard)
There used to be unrest in the Galactic Senate. Several thousand solar systems had declared their intentions to leave the Republic. But fear not! Master Heidi was able to successfully select the Jedi Knights that have restored peace in the galaxy. However, she knows that evil never sleeps and a time may come when she will need to pick another group of Jedi Knights. She wants to be sure she has enough options to do so.
There are $ n $ Jedi Knights, each of them with a lightsaber of one of $ m $ colors. Given a number $ k $ , compute the number of differently colored collections of $ k $ lightsabers that some $ k $ Jedi Knights might have. Jedi Knights with lightsabers of the same color are indistinguishable (it's not the person, it's the lightsaber color that matters!), and their order does not matter; that is, we consider two collections of Jedi Knights to be different if and only if their vectors of counts of lightsabers of each color (like what you were given in the easy and the medium versions) are different. We count all subsets, not only contiguous subsegments of the input sequence. Output the answer modulo $ 1009 $ .
Input
The first line of the input contains $ n $ ( $ 1<=n<=2·10^{5} $ ), $ m $ ( $ 1<=m<=n $ ) and $ k $ ( $ 1<=k<=n $ ). The second line contains $ n $ integers in the range $ {1,2,...,m} $ representing colors of the lightsabers of subsequent Jedi Knights.
Output
Output one number: the number of differently colored collections of $ k $ lightsabers modulo $ 1009 $ .
想要求本质不同 \(k\) 子集的个数,我们发现之和一个元素选取了多少个有关,因此我们考虑生成函数 \(F_a(x) = \sum\limits_{i = 0}^{cnt_a}x^i\) 钦定每种元素选了多少个,显然
我们想要的答案就在 \(G(x)\) 的 \(x^k\) 系数上。
但是暴力去乘 \(O(n^2\log{n})\) 是不可能了,我们利用分治,每次合并 \([l, \, mid]\) 和 \([mid + 1, \, r]\) 的一段,根据主定理 \(T(n) = 2T(\frac{n}{2}) + O(n\log{n}) = O(n\log^2{n})\),因此可以通过。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define clr(f, n) memset(f, 0, sizeof(Complex) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(Complex) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 2e5 + 10, mod = 1009;
const ld Pi = acos(-1);
struct Complex {
ld x, y;
Complex (ld xx = 0, ld yy = 0) {x = xx, y = yy;}
Complex operator+ (Complex const &C) const {return Complex(x + C.x, y + C.y);}
Complex operator+ (ld R) {return Complex(x + R, y);}
Complex operator- (Complex const &C) const {return Complex(x - C.x, y - C.y);}
Complex operator- (ld R) {return Complex(x - R, y);}
Complex operator* (Complex const &C) const {return Complex(x * C.x - y * C.y, x * C.y + y * C.x);}
Complex operator/ (const ld &R) const {return Complex(x / R, y / R);}
Complex operator~ () {return Complex(x, -y);}
};
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void FFT(Complex *f, int op, int n) {
rev_init(n);
for (int i = 0; i < n; i ++ ) if (i < rev[i]) swap(f[i], f[rev[i]]);
for (int k = 1; k < n; k <<= 1) {
Complex w1 = Complex(cos(Pi / k), op * sin(Pi / k));
for (int i = 0; i < n; i += k << 1) {
Complex wk = Complex(1, 0);
for (int j = 0; j < k; j ++ , wk = wk * w1) {
Complex x = f[i + j], y = wk * f[i + j + k];
f[i + j] = x + y, f[i + j + k] = x - y;
}
}
}
if (op == -1) for (int i = 0; i < n; i ++ ) f[i] = f[i] / n;
}
void px(Complex *f, Complex *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = f[i] * g[i];
}
void covolution(Complex *f, Complex *g, int len, int lim) {
static Complex sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
FFT(sav, 1, n); FFT(f, 1, n);
px(f, sav, n); FFT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
int n, m, k, a[N], cnt[N];
vector<ll> F[N];
void calc(int l, int r) {
static Complex b1[N << 1], b2[N << 1];
if (l == r) return;
int mid = l + r >> 1;
calc(l, mid); calc(mid + 1, r);
int len1 = F[l].size(), len2 = F[mid + 1].size();
for (int i = 0; i < len1; i ++ ) b1[i] = Complex(F[l][i], 0);
for (int i = 0; i < len2; i ++ ) b2[i] = Complex(F[mid + 1][i], 0);
covolution(b1, b2, len1 + len2 >> 1, len1 + len2 - 1);
F[l].resize(len1 + len2 - 1);
for (int i = 0; i < len1 + len2 - 1; i ++ ) F[l][i] = (ll)(b1[i].x + 0.5) % mod;
clr(b1, len1 + len2 - 1); clr(b2, len1 + len2 - 1);
}
void solve() {
cin >> n >> m >> k;
for (int i = 0; i < n; i ++ ) cin >> a[i], cnt[a[i]] ++ ;
for (int i = 1; i <= m; i ++ ) F[i - 1].resize(cnt[i] + 1, 1);
calc(0, m - 1);
cout << F[0][k] % mod << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
Emma and sum of products
Emma真的喜欢整数,并且喜欢和它们玩。 她的朋友们嫉妒她,为了考考她,一个朋友给了她一个问题。
给Emma一个包含N个整数的列表A,和Q个查询。每个查询由一个整数K表示,你需要返回所有恰好包含K个元素的子列表元素乘积的和。Emma被这个问题难住了,作为她最好的朋友,你决定帮她写程序完成解决这个问题。因为结果可能非常大,输出它对100003取余数的结果。
输入格式:
第一行包含一个整数N,表示列表A中的整数个数。下一行包含N个空格分隔的整数。第三行包含整数Q,接下来Q行每行包含一个整数K。
输出格式:
对每个查询,在一行中输出相应的结果。
我们如果把答案写作 \(F(x) = \sum_{i = 1}^nAns_ix^i\) 的形态,容易看出选择 \(k\) 个数相当于 \(k\) 个数的生成函数系数相乘起来再相加,于是我们可以构造 \(G_i(x) = 1 + a_ix^i\),将 \(n\) 个多项式相乘即可,这里的套路还是相同,利用启发式合并,时间复杂度可以做到 \(O(n\log^2{n})\)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(Complex) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(Complex) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 1e5 + 10, mod = 100003;
const ld Pi = acos(-1);
struct Complex {
ld x, y;
Complex (ld xx = 0, ld yy = 0) {x = xx, y = yy;}
Complex operator+ (Complex const &C) const {return Complex(x + C.x, y + C.y);}
Complex operator+ (ld R) {return Complex(x + R, y);}
Complex operator- (Complex const &C) const {return Complex(x - C.x, y - C.y);}
Complex operator- (ld R) {return Complex(x - R, y);}
Complex operator* (Complex const &C) const {return Complex(x * C.x - y * C.y, x * C.y + y * C.x);}
Complex operator/ (const ld &R) const {return Complex(x / R, y / R);}
Complex operator~ () {return Complex(x, -y);}
};
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void FFT(Complex *f, int op, int n) {
rev_init(n);
for (int i = 0; i < n; i ++ ) if (i < rev[i]) swap(f[i], f[rev[i]]);
for (int k = 1; k < n; k <<= 1) {
Complex w1 = Complex(cos(Pi / k), op * sin(Pi / k));
for (int i = 0; i < n; i += k << 1) {
Complex wk = Complex(1, 0);
for (int j = 0; j < k; j ++ , wk = wk * w1) {
Complex x = f[i + j], y = wk * f[i + j + k];
f[i + j] = x + y, f[i + j + k] = x - y;
}
}
}
if (op == -1) for (int i = 0; i < n; i ++ ) f[i] = f[i] / n;
}
void px(Complex *f, Complex *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = f[i] * g[i];
}
void covolution(Complex *f, Complex *g, int len, int lim) {
static Complex sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
FFT(sav, 1, n); FFT(f, 1, n);
px(f, sav, n); FFT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
int n, q, a[N];
vector<int> cnt[N];
void calc(int l, int r) {
if (l == r) return;
static Complex f[N << 1];
int mid = l + r >> 1;
calc(l, mid); calc(mid + 1, r);
int n = cnt[l].size(), m = cnt[mid + 1].size();
for (int i = 0; i < n; i ++ ) f[i].x = cnt[l][i];
for (int i = 0; i < m; i ++ ) f[i].y = cnt[mid + 1][i];
covolution(f, f, n + m >> 1, n + m - 1);
cnt[l].resize(n + m - 1);
for (int i = 0; i < n + m - 1; i ++ ) cnt[l][i] = (ll)(f[i].y / 2 + 0.5) % mod;
clr(f, n + m - 1);
}
void solve() {
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> a[i], cnt[i] = {1, a[i]};
calc(0, n - 1);
cin >> q;
while (q -- ) {
int t;
cin >> t;
cout << cnt[0][t] << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
P4173 残缺的字符串
很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串 \(A\) 和 \(B\),其中 \(A\) 串长度为 \(m\),\(B\) 串长度为 \(n\)。可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺。
你想对这两个串重新进行匹配,其中 \(A\) 为模板串,那么现在问题来了,请回答,对于 \(B\) 的每一个位置 \(i\),从这个位置开始连续 \(m\) 个字符形成的子串是否可能与 \(A\) 串完全匹配?
\(100\%\) 的数据满足 \(1 \le m \le n \le 3 \times 10^5\)。
输入格式
第一行包含两个正整数 \(m,n\),分别表示 \(A\) 串和 \(B\) 串的长度。
第二行为一个长度为 \(m\) 的字符串 \(A\)。
第三行为一个长度为 \(n\) 的字符串 \(B\)。
两个串均仅由小写字母和 \(\texttt *\) 组成,其中 \(\texttt *\) 表示相应位置已经残缺。
输出格式
第一行包含一个整数 \(k\),表示 \(B\) 串中可以完全匹配 \(A\) 串的位置个数。
若 \(k>0\),则第二行输出 \(k\) 个正整数,从小到大依次输出每个可以匹配的开头位置(下标从 \(1\) 开始)。
对于含有通配字符的匹配问题,设通配字符为 \(0\),我们构造匹配函数
想要进行匹配,我们不难想到
其实也就是
到这里变得套路起来,如果我们将 \(P\) 串翻转,那么有
那么我们可以利用卷积完成函数的运算,最后检测位置是否为 \(0\) 即可。
需要注意把 a
映射到 \(1\),不然会爆精度。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(Complex) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(Complex) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 6e5 + 10, mod = 100003;
const ld Pi = acos(-1);
struct Complex {
ld x, y;
Complex (ld xx = 0, ld yy = 0) {x = xx, y = yy;}
Complex operator+ (Complex const &C) const {return Complex(x + C.x, y + C.y);}
Complex operator+ (ld R) {return Complex(x + R, y);}
Complex operator- (Complex const &C) const {return Complex(x - C.x, y - C.y);}
Complex operator- (ld R) {return Complex(x - R, y);}
Complex operator* (Complex const &C) const {return Complex(x * C.x - y * C.y, x * C.y + y * C.x);}
Complex operator/ (const ld &R) const {return Complex(x / R, y / R);}
Complex operator~ () {return Complex(x, -y);}
};
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void FFT(Complex *f, int op, int n) {
rev_init(n);
for (int i = 0; i < n; i ++ ) if (i < rev[i]) swap(f[i], f[rev[i]]);
for (int k = 1; k < n; k <<= 1) {
Complex w1 = Complex(cos(Pi / k), op * sin(Pi / k));
for (int i = 0; i < n; i += k << 1) {
Complex wk = Complex(1, 0);
for (int j = 0; j < k; j ++ , wk = wk * w1) {
Complex x = f[i + j], y = wk * f[i + j + k];
f[i + j] = x + y, f[i + j + k] = x - y;
}
}
}
if (op == -1) for (int i = 0; i < n; i ++ ) f[i] = f[i] / n;
}
void px(Complex *f, Complex *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = f[i] * g[i];
}
void covolution(Complex *f, Complex *g, int len, int lim) {
static Complex sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
FFT(sav, 1, n); FFT(f, 1, n);
px(f, sav, n); FFT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
int n, m;
string s, p;
Complex F[N << 1];
ll cnt[N];
vector<int> ans;
void solve() {
cin >> m >> n;
cin >> p >> s;
reverse(p.begin(), p.end());
for (int i = 0; i < n; i ++ ) s[i] = (s[i] == '*' ? 0 : s[i] - 'a' + 1);
for (int i = 0; i < m; i ++ ) p[i] = (p[i] == '*' ? 0 : p[i] - 'a' + 1);
for (int i = 0; i < n; i ++ ) F[i].x = s[i] * s[i] * s[i];
for (int i = 0; i < m; i ++ ) F[i].y = p[i];
covolution(F, F, n + m >> 1, n + m - 1);
for (int i = 0; i < n + m - 1; i ++ ) cnt[i] += (ll)(F[i].y / 2 + 0.5);
clr(F, n + m - 1);
for (int i = 0; i < n; i ++ ) F[i].x = s[i] * s[i];
for (int i = 0; i < m; i ++ ) F[i].y = p[i] * p[i];
covolution(F, F, n + m >> 1, n + m - 1);
for (int i = 0; i < n + m - 1; i ++ ) cnt[i] -= (ll)(F[i].y / 2 + 0.5) << 1;
clr(F, n + m - 1);
for (int i = 0; i < n; i ++ ) F[i].x = s[i];
for (int i = 0; i < m; i ++ ) F[i].y = p[i] * p[i] * p[i];
covolution(F, F, n + m >> 1, n + m - 1);
for (int i = 0; i < n + m - 1; i ++ ) cnt[i] += (ll)(F[i].y / 2 + 0.5);
for (int i = m - 1; i < n; i ++ ) if (!cnt[i]) ans.pb(i - m + 2);
cout << ans.size() << "\n";
for (auto i : ans) cout << i << " \n"[i == ans.back()];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
CF827E Rusty String
Grigory loves strings. Recently he found a metal strip on a loft. The strip had length $ n $ and consisted of letters "V" and "K". Unfortunately, rust has eaten some of the letters so that it's now impossible to understand which letter was written.
Grigory couldn't understand for a long time what these letters remind him of, so he became interested in the following question: if we put a letter "V" or "K" on each unreadable position, which values can the period of the resulting string be equal to?
A period of a string is such an integer $ d $ from $ 1 $ to the length of the string that if we put the string shifted by $ d $ positions to the right on itself, then all overlapping letters coincide. For example, $ 3 $ and $ 5 $ are periods of "VKKVK".
Input
There are several (at least one) test cases in the input. The first line contains single integer — the number of test cases.
There is an empty line before each test case. Each test case is described in two lines: the first line contains single integer $ n $ ( $ 1<=n<=5·10^{5} $ ) — the length of the string, the second line contains the string of length $ n $ , consisting of letters "V", "K" and characters "?". The latter means the letter on its position is unreadable.
It is guaranteed that the sum of lengths among all test cases doesn't exceed $ 5·10^{5} $ .
For hacks you can only use tests with one test case.
Output
For each test case print two lines. In the first line print the number of possible periods after we replace each unreadable letter with "V" or "K". In the next line print all these values in increasing order.
有了上题的铺垫,我们假设 \(S[1 \sim k]\) 是周期,那么 \(S[tk + 1 \sim (t+1)k]\),也是它的周期,那么我们将匹配串平移 \(k\) 位,依旧能够匹配,如果设 \(?\) 表示通配字符,那么这题的做法似乎可以套用上题的做法。
当你写了之后,你发现并没有这么简单,因为这里的 ?
是未知字符,他只能代表 V
或 K
中的一种,于是,只有 \(S(i) = S(i + k) = \cdots = S(i + tk)\) 的时候才成立。
但是恰好,由于我们求出了所有可能的 \(k\) 值,那么如果 \(k\) 的倍数都合法,也就告诉我们 \(S(i) = S(i + k) = \cdots = S(i + tk)\),因此对于每个 \(k\),枚举它的倍数是否合法即可,时间复杂度 \(O(n\ln{n})\)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 1.5e6 + 10, _G = 3, mod = 998244353;
ll qpow(ll a, ll k = mod - 2) {
ll res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
const int invG = qpow(_G);
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void NTT(int *g, int op, int n) {
rev_init(n);
static ull f[N << 1], Gk[N << 1] = {1};
for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
for (int k = 1; k < n; k <<= 1) {
int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
for (int i = 0; i < n; i += k << 1) {
for (int j = 0; j < k; j ++ ) {
int tmp = Gk[j] * f[i | j | k] % mod;
f[i | j | k] = f[i | j] + mod - tmp;
f[i | j] += tmp;
}
}
if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
}
if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
else {
int invn = qpow(n);
for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
}
}
void px(int *f, int *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}
void covolution(int *f, int *g, int len, int lim) {
static int sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
NTT(sav, 1, n); NTT(f, 1, n);
px(f, sav, n); NTT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
int n, F[N << 1], G[N << 1], cnt[N], st[N];
string s;
vector<int> ans;
void solve() {
cin >> n >> s;
for (int i = 0; i < n; i ++ ) {
if (s[i] == '?') s[i] = 0;
else if (s[i] == 'V') s[i] = 1;
else s[i] = 2;
}
for (int i = 0; i < n; i ++ ) F[i] = s[i] * s[i] * s[i], G[n - i - 1] = s[i];
covolution(F, G, n, (n << 1) - 1);
for (int i = 1; i <= n; i ++ ) cnt[i] += F[i + n - 1];
clr(F, (n << 1) - 1); clr(G, (n << 1) - 1);
for (int i = 0; i < n; i ++ ) F[i] = s[i] * s[i], G[n - i - 1] = s[i] * s[i];
covolution(F, G, n, (n << 1) - 1);
for (int i = 1; i <= n; i ++ ) cnt[i] -= F[i + n - 1] << 1;
clr(F, (n << 1) - 1); clr(G, (n << 1) - 1);
for (int i = 0; i < n; i ++ ) F[i] = s[i], G[n - i - 1] = s[i] * s[i] * s[i];
covolution(F, G, n, (n << 1) - 1);
for (int i = 1; i <= n; i ++ ) cnt[i] += F[i + n - 1];
clr(F, (n << 1) - 1); clr(G, (n << 1) - 1);
for (int i = 1; i <= n; i ++ ) st[i] = !(cnt[i] % mod);
for (int i = 1; i <= n; i ++ ) {
if (!st[i]) continue;
int flag = 1;
for (int j = i << 1; j <= n; j += i) {
if (!st[j]) {
flag = 0;
break;
}
}
if (flag) ans.pb(i);
}
cout << ans.size() << "\n";
for (auto i : ans) cout << i << " \n"[i == ans.back()];
clr(cnt, n + 1); clr(st, n + 1); ans.clear();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}
CF528D Fuzzy Search
给出一个门限值 \(k\) 和两个只包含 \(\texttt{AGCT}\) 四种字符的基因串 \(S\) 和 \(T\)。现在你要找出在下列规则中 \(T\) 在 \(S\) 中出现了几次。
\(T\) 在 \(S\) 的第 \(i\) 个位置中出现,当且仅当把 \(T\) 的首字符和 \(S\) 的第 \(i\) 个字符对齐后,\(T\) 中的每一个字符能够在 \(S\) 中找到一个位置偏差不超过 \(k\) 的相同字符。
即对于所有的 \(j \in[1,|T|]\),都存在一个 \(p \in [1,|S|]\) 使得 \(|(i+j-1)-p| \leq k\) 且 \(S_p=T_j\) 。
例如 \(k=1\) 时,\(\texttt{ACAT}\) 出现在 \(\texttt{AGCAATTCAT}\) 的 \(2\) 号, \(3\) 号和 \(6\) 号位置。 (编号从 \(1\) 开始。)
输入格式
第一行有三个整数 \(n\) , \(m\) , \(k\) ,表示 \(S\) 的长度, \(T\) 的长度和"门限值"。
第二行给出基因串\(S\),第三行给出基因串\(T\),且两个串都只包含大写字母\(\texttt{ATGC}\) 。
\(1≤n≤m≤2*10^5\ ,\ 0≤k≤2*10^5\)
输出格式
输出一个整数,表示 \(T\) 在 \(S\) 中出现的次数。
字符串匹配老套路,我们想要匹配的时候,因为一个字符可以匹配距离为 \(k\) 的字符,所以我们不妨在预处理字符串的时候将每个字符扩展 \(k\) 位,然后再做 FFT 加起来,注意到每一个位置对固定位置的匹配贡献为 \(1\),所以我们只需要卷积后比较每个字符的贡献是否为匹配串对应字符的个数即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 1.1e6 + 10, _G = 3, mod = 998244353;
ll qpow(ll a, ll k = mod - 2) {
ll res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
const int invG = qpow(_G);
int rev[N << 1], rev_len;
void rev_init(int n) {
if (rev_len == n) return;
for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
rev_len = n;
}
void NTT(int *g, int op, int n) {
rev_init(n);
static ull f[N << 1], Gk[N << 1] = {1};
for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
for (int k = 1; k < n; k <<= 1) {
int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
for (int i = 0; i < n; i += k << 1) {
for (int j = 0; j < k; j ++ ) {
int tmp = Gk[j] * f[i | j | k] % mod;
f[i | j | k] = f[i | j] + mod - tmp;
f[i | j] += tmp;
}
}
if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
}
if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
else {
int invn = qpow(n);
for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
}
}
void px(int *f, int *g, int n) {
for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}
void covolution(int *f, int *g, int len, int lim) {
static int sav[N << 1];
int n; for (n = 1; n < len << 1; n <<= 1);
clr(sav, n); cpy(sav, g, n);
NTT(sav, 1, n); NTT(f, 1, n);
px(f, sav, n); NTT(f, -1, n);
clr(f + lim, n - lim), clr(sav, n);
}
int n, m, k, cnt[N];
int F[4][N << 1], G[4][N << 1];
string s, t;
void solve() {
cin >> n >> m >> k;
cin >> s >> t;
for (int i = 0; i < n; i ++ ) {
if (s[i] == 'A') F[0][i] = 1;
else if (s[i] == 'C') F[1][i] = 1;
else if (s[i] == 'G') F[2][i] = 1;
else F[3][i] = 1;
}
reverse(t.begin(), t.end());
for (int i = 0; i < m; i ++ ) {
if (t[i] == 'A') G[0][i] = 1, cnt[0] ++ ;
else if (t[i] == 'C') G[1][i] = 1, cnt[1] ++ ;
else if (t[i] == 'G') G[2][i] = 1, cnt[2] ++ ;
else G[3][i] = 1, cnt[3] ++ ;
}
for (int i = 0; i < 4; i ++ ) {
set<int> S;
int last = 0;
for (int j = 0; j < n; j ++ ) {
if (!F[i][j]) continue;
for (int p = max(last, j - k); p < min(n, j + k + 1); p ++ ) S.insert(p);
last = j + k + 1;
}
for (auto it : S) F[i][it] = 1;
covolution(F[i], G[i], n + m >> 1, n + m - 1);
}
int ans = 0;
for (int i = m - 1; i < n; i ++ ) {
int flag = 1;
for (int j = 0; j < 4; j ++ ) {
if (F[j][i] < cnt[j]) {
flag = 0;
break;
}
}
if (flag) ans ++ ;
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}