多项式例题4
P4389 付公主的背包
有一个背包最多可以装 \(10^5\) 大小的东西
付公主有 \(n\) 种商品,她要准备出摊了
每种商品体积为 \(v_i\),都有无限件
给定 \(m\),对于 \(s\in [1,m]\),请你回答用这些商品恰好装 \(s\) 体积的方案数
输入格式
第一行两个正整数 \(n,m\)。
第二行 \(n\) 个正整数,表示每种商品的体积。
输出格式
输出 \(m\) 行,第 \(i\) 行代表 \(s=i\) 时方案数,对 \(998244353\) 取模。
数据范围
对于 \(30\%\) 的数据,\(1\le n,m \le 3000\);
对于 \(60\%\) 的数据,纯随机生成;
对于 \(100\%\) 的数据, \(1\le n,m \le 10^5\),\(1\le v_i \le m\)。
不失一般性,做这个背包的时候有一个显然的生成函数乘积表达式,
但是这个乘积是 \(O(n^2\log{n})\) 的复杂度,无法接受。
我们将生成函数写成封闭形式即
取对数之后我们发现
看起来很接近答案了,但是求 \(\ln\) 的复杂度还是无法接受,我们看看这个 \(\ln\) 式可不可以写得更好看一些,考虑 \(\ln F(x) = G(x)\),两边同时求导有
实际上你可以发现,这个东西相当于 \(-\ln(1 - x^k)\) 的麦克劳林级数。
Amazing 啊,对于所有的 \(k\) 我们的复杂度是 \(O(n\ln{n})\) 的,然后使用多项式 \(\exp\) 干回去就好了。
#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 = 2e5 + 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 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 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);
}
int n, m, a[N], cnt[N];
int F[N << 1];
void solve() {
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> a[i], cnt[a[i]] ++ ;
inv_init(m);
for (int i = 1; i <= m; i ++ ) {
for (int j = 1; i * j <= m; j ++ ) {
(F[i * j] += 1ll * inv[j] * cnt[i] % mod) %= mod;
}
}
Poly_exp(F, m + 1);
for (int i = 1; i <= m; i ++ ) cout << F[i] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
CF1257G Divisor Set
You are given an integer $ x $ represented as a product of $ n $ its prime divisors $ p_1 \cdot p_2, \cdot \ldots \cdot p_n $ . Let $ S $ be the set of all positive integer divisors of $ x $ (including $ 1 $ and $ x $ itself).
We call a set of integers $ D $ good if (and only if) there is no pair $ a \in D $ , $ b \in D $ such that $ a \ne b $ and $ a $ divides $ b $ .
Find a good subset of $ S $ with maximum possible size. Since the answer can be large, print the size of the subset modulo $ 998244353 $ .
Input
The first line contains the single integer $ n $ ( $ 1 \le n \le 2 \cdot 10^5 $ ) — the number of prime divisors in representation of $ x $ .
The second line contains $ n $ integers $ p_1, p_2, \dots, p_n $ ( $ 2 \le p_i \le 3 \cdot 10^6 $ ) — the prime factorization of $ x $ .
Output
Print the maximum possible size of a good subset modulo $ 998244353 $ .
根据 Sperner 引理,从 \(n\) 元集合中选择大小为 \(\lfloor\frac{n}{2}\rfloor\) 可以使得互不包含子集个数最多,考虑每个质数的生成函数 \(F(x) = \sum\limits_{i = 0}^{c_p}x^{ip}\),其总表达式为
这不禁让我们考虑 \(\ln\) 后求和
这两部分的麦克劳林级数是已知的
考虑到我们只用枚举所有的 \(c_p\),求出多项式的 \(\exp\) 在 \(x^{\lfloor\frac{n}{2}\rfloor}\) 的系数即可.
#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 = 4e5 + 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 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 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);
}
int n, m, a[N], c[N];
int F[N << 1];
vector<int> alls;
unordered_map<int, int> mp;
void solve() {
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> a[i], mp[a[i]] ++ ;
inv_init(n);
for (auto [x, y] : mp) c[y + 1] ++ , m ++ ;
for (int i = 1; i <= n; i ++ ) {
(F[i] += 1ll * m * inv[i] % mod) %= mod;
if (!c[i]) continue;
for (int j = 1; i * j <= n >> 1; j ++ ) {
(F[i * j] += mod - 1ll * c[i] * inv[j] % mod) %= mod;
}
}
Poly_exp(F, (n >> 1) + 1);
cout << F[n >> 1] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
P4841 [集训队作业2013] 城市规划
刚刚解决完电力网络的问题,阿狸又被领导的任务给难住了。
刚才说过,阿狸的国家有 \(n(n \le 130000)\) 个城市,现在国家需要在某些城市对之间建立一些贸易路线,使得整个国家的任意两个城市都直接或间接的连通。
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径。对于两个建立路线的方案,如果存在一个城市对,在两个方案中是否建立路线不一样,那么这两个方案就是不同的,否则就是相同的。现在你需要求出一共有多少不同的方案。
好了,这就是困扰阿狸的问题。换句话说,你需要求出 \(n\) 个点的简单 (无重边无自环) 有标号无向连通图数目。
由于这个数字可能非常大, 你只需要输出方案数对 \(1004535809\) ( \(479 \times 2 ^{21} + 1\) ) 取模即可。
输入格式
仅一行一个整数 \(n\)
输出格式
仅一行一个整数,为方案数 \(\bmod \space 1004535809\)。
设 \(f_n\) 表示大小为 \(n\) 的 有标号无向连通图 方案数,\(g_n\) 表示大小为 \(n\) 的 有标号无向图 方案数,显然有 \(g_n = 2^{\binom{n}{2}}\),通过钦定 \(1\) 号节点所在联通块的大小为 \(i\),它们之间具有关系
也就是说
而
移项可得
不难看出右式是一个卷积形式,这告诉我们
设 \(F(x) = \sum\limits_{i = 0}^{n - 1}\frac{2^{\binom{i}{2}}}{i!}x^i\),\(G(x) = \sum\limits_{i = 1}^n\frac{f_i}{(i - 1)!}x^i\),\(H(x) = \sum\limits_{i = 1}^{n}\frac{2^{\binom{i}{2}}}{(i - 1)!}x^i\),那么有 \(F(x)G(x) = H(x)\),求逆即可。
#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 = 4e5 + 10, _G = 3, mod = 1004535809;
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_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);
}
int n, m, F[N], G[N];
int pow2[N], frac[N], inv[N];
void solve() {
cin >> n;
frac[0] = inv[0] = 1;
for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod;
inv[n] = qpow(frac[n]);
for (int i = n - 1; i; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
for (int i = 0; i <= n; i ++ ) pow2[i] = qpow(2, 1ll * i * (i - 1) / 2);
for (int i = 0; i < n; i ++ ) F[i] = 1ll * pow2[i] * inv[i] % mod;
for (int i = 1; i <= n; i ++ ) G[i] = 1ll * pow2[i] * inv[i - 1] % mod;
Poly_inv(F, n); covolution(F, G, n + 1, n + 1);
cout << 1ll * F[n] * frac[n - 1] % 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;
}
gym104386G CLC Loves SQRT Technology (Hard Version)
This is the hard version of the problem. The only difference between the two versions is the constraint on \(n\).
You have an array of length \(n\), We declare function \(f(x)\) for a non-empty subsequence of the array as the minimum number of moves needed to change the subsequence's elements to make the subsequence \(palindrome\). The move is defined as follows.
- Select an element from the subsequence, and change the value to any arbitrary value.
Now your task is to find sum of \(f(x)\) for all non-empty subsequences of the array.
Input
In the first line of input You'll be given number \(n\) which shows the length of the array.
In the second line You'll receive the array.
\(1 \le n \le 10^5\).
\(1 \le a_i \le n\).
Output
Print an integer representing the sum of \(f(x)\) for all non-empty subsequences, as the answer may be large, Print it modulo \(998244353\).
枚举两个位置的数 \(a_i, \, a_j(i < j)\),那么会产生的贡献为
这是一个范德蒙德卷积,原式可规约为
更进一步的,这个不等关系不够优秀,我们考虑改为相等关系,对于任意一个排列,其要修改的次数为排列长度的一半,因此可以枚举排列长度
第一部分可以直接爆算出来,考虑后面部分如何计算,对于相同的 \(a_i = a_j\),我们看第二部分展开如何计算
考虑 \(F(x) = \sum\limits_{i = 0}^{n - 1}\frac{1}{2^{i + 2}i!}x^{i}\),\(G(x) = \sum\limits_{j = 0}^{n - 1}\frac{2^{n - j}}{j!}x^j\),给 \(a_i\) 打到 \([x^{i - 1}]F(x)\) 上,\(a_j\) 打到 \([x^{n - j}]G(x)\) 上,卷积后可以发现 \(a_i = a_j\) 的贡献会被打到 \([x^{n - j + i - 1}]H(x)\) 上,而因为 \(j > i\) 的组才满足条件,我们又恰好发现 \(n - j + i - 1 < n - 1\),因此我们合法的对会卷到 \([0, \, n - 2]\) 内,对于所有卷出来的点系数 \([x^{n - j + i - 1}]H(x)\) 恰好差一个 \((n - j + i - 1)!\) 的因子,因此我们枚举所有 \(k \in [0, \, n - 2]\),点系数乘上 \(k!\) 就是我们所求贡献。
好了直到如此,我们得到了一个 \(O(Bn\log{n})\) 的算法,其中 \(B\) 为元素的不同种类数,而暴力范德蒙德卷积针对相同元素有一个 \(O(K^2)\) 的算法,其中 \(K\) 为相同元素的个数,这启示我们需要进行复杂度平衡,我们设 \(K\) 足够均匀,那么有 \(B = \frac{n}{K}\),则两组算法的复杂度均摊为 \(O(\frac{n^2}{K}\log{n})\) 和 \(O(K^2)\),取 \(K = \sqrt{n\log{n}}\) 有总复杂度 \(O(n\sqrt{n\log{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 = 2e5 + 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);
}
const int i2 = qpow(2), B = 1300;
int n, m, a[N];
int frac[N], inv[N], pow2[N], inv2[N];
int F[N << 1], G[N << 1];
vector<int> pos[N];
ll C(int n, int m) {
if (n < m) return 0;
return 1ll * frac[n] * inv[m] % mod * inv[n - m] % mod;
}
ll solve1(vector<int> &pos) {
int len = pos.size();
ll res = 0;
for (int i = 0; i < len; i ++ ) {
F[pos[i] - 1] = 1ll * inv2[pos[i] + 1] * inv[pos[i] - 1] % mod;
G[n - pos[i]] = 1ll * pow2[pos[i]] * inv[n - pos[i]] % mod;
}
covolution(F, G, n, n);
for (int i = 0; i <= n - 2; i ++ ) res += 1ll * F[i] * frac[i] % mod;
clr(F, n); clr(G, n);
return res % mod;
}
ll solve2(vector<int> &pos) {
int len = pos.size();
ll res = 0;
for (int i = 0; i < len; i ++ ) {
for (int j = i + 1; j < len; j ++ ) {
res += C(n - pos[j] + pos[i] - 1, pos[i] - 1) * pow2[pos[j] - pos[i] - 1] % mod;
}
}
return res % mod;
}
void solve() {
cin >> n;
frac[0] = inv[0] = pow2[0] = inv2[0] = 1;
for (int i = 1; i < N; i ++ ) {
frac[i] = 1ll * frac[i - 1] * i % mod;
pow2[i] = (pow2[i - 1] << 1) % mod;
inv2[i] = 1ll * inv2[i - 1] * i2 % mod;
}
inv[N - 1] = qpow(frac[N - 1]);
for (int i = N - 2; i; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
for (int i = 1; i <= n; i ++ ) cin >> a[i], pos[a[i]].pb(i);
ll ans = 0;
for (int i = 1; i <= n; i ++ ) ans += C(n, i) * (i >> 1) % mod;
for (int i = 1; i <= n; i ++ ) {
if (pos[i].size() < 2) continue;
if (pos[i].size() > B) ans -= solve1(pos[i]);
else ans -= solve2(pos[i]);
}
cout << (ans % mod + mod) % 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;
}
gym105891H candy
Xiaoming is a little kid who loves eating candy and sleeping.
Because eating too much candy can cause cavities, Xiaoming has made an \(n\)-day candy-eating plan for himself.
Xiaoming gets sad when he can't eat candy, so he uses a mood level to represent his mood each day. On day \(0\), his mood is \(0\).
Xiaoming's candy-eating plan is described by a string \(s\) of length \(n\) containing only \(\texttt{01}\). For the \(i\)-th day of the plan:
- If \(s_i=\texttt{1}\), it means Xiaoming will eat a piece of candy on the \(i\)-th day, and his mood will increase by \(1\);
- If \(s_i=\texttt{0}\), it means Xiaoming will not eat candy on the \(i\)-th day to prevent cavities, and his mood will decrease by \(1\).
Xiaoming believes that the higher his mood, the more perfect the plan is. Therefore, the perfection of the plan is defined as the maximum mood level he reaches during days \(0\) to \(n\).
However, since Xiaoming is a sleepy little kid, each day he has a \(\dfrac{1}{2}\) probability of sleeping and skipping his plan for the day, and a \(\dfrac{1}{2}\) probability of following the plan as usual.
If Xiaoming chooses to sleep on a certain day, he will not eat candy, and his mood will remain unchanged.
Xiaoming's decisions each day are independent (sleeping or following the plan). Please help him calculate the expected perfection of the plan.
Since the answer may not be an integer, you only need to output the answer modulo \(998244353\).
Specifically, the answer can always be expressed as a rational number \(\dfrac{p}{q}\), where \(p,q\) are coprime, and \(q\) is not a multiple of \(998244353\).
You need to find the unique integer \(x\) satisfying \(x\times q\equiv 1\pmod {998244353}\), and then output the value of \(xp\bmod 998244353\).
Input
The first line contains an integer \(n(1\le n\le 5\times 10^5)\), representing the number of days in the plan.
The second line contains a string of length \(n\) containing only \(\texttt{01}\), describing Xiaoming's plan.
Output
Output a single integer representing the expected perfection of the plan modulo \(998244353\).
我们考虑转化为求方案数问题,将 \(0\) 替换为 \(-1\) 后,不难发现,求一个序列的最大前缀和,相当于先给这个序列做一个前缀和,然后求这个序列的严格前缀最大值个数。
考虑拆贡献,枚举每一个位置 \(i\),统计有多少个子序列包含 \(i\) 这个位置,并且 \(i\) 这个位置的前缀和严格大于前面所有位置的前缀和。
不难发现,\(i\) 的前缀和是严格前缀最大值,相当于 \(a_i=1\),并且 \(i-1\) 的前缀和是(可以不严格的)前缀最大值。
设 \(dp_{i, \, j}\) 表示从前i个位置中选了若干位置组成一个子序列,对于选出的序列,设其最大前缀和为 \(x\),整个序列的和为 \(y\),满足 \(x - y = j\) 的方案数。
不难发现,若 \(j=0\),则表示整个序列的和与最大前缀和的值相等,此时只需要在序列末尾添加一个 \(1\),整个序列的权值就会增加 \(1\)。
初始状态为 \(dp_{0, \, 0} = 1\),考虑 \(dp_{i-1, \, j}\) 能往哪些位置转移。
- 没有选 \(i\) 这个位置,有转移 \(dp_{i-1, \, j} \to dp_{i, \, j}\);
- 选了 \(i\) 这个位置,按照 \(a_i\) 进行分类讨论:
- \(a_i = 1\),则序列的和会增加 \(1\),整个序列的权值有可能增加 \(1\),有转移 \(dp_{i-1, \, j} \to dp_{i, \, \max(0, \, j-1)}\);
- \(a_i = -1\),则序列的和会减少 \(1\),整个序列的权值一定不变,有转移 \(dp_{i-1, \, j} \to dp_{i, \, j+1}\) 。
答案即为 \(\sum\limits_{a_i = 1} dp_{i-1, \, 0} × 2^{n-i}\) 。
时间复杂度为 \(O(n^2)\)。
接下来我们考虑优化 \(dp\),考虑我们如果求出了 \(dp_{l - 1}\) 的所有值,我们能否通过分治算法求得 \(dp_{r}\) 的值。
可以观察到一些性质,对于所有 \(\le 1\) 的 \(dp_{i, \, j}\),他们的转移都是比较“整齐”的。
什么叫整齐呢,具体一点,对于一个 \(i\),要么所有的都同时往 \(j, \, j-1\) 这两个位置转移,要么所有的都同时往 \(j, \, j+1\) 这两个位置转移,当且仅当 \(j = 0\) 时自己向自己转移,我们考虑多项式优化,其余部分的转移相当于是整体卷上了一个 \(x + 1\) 外带一个偏移量,具体的:元素为 \(1\),乘上 \(x + 1\),元素为 \(0\),乘上 \((x + 1)x^{-1}\)。
不难发现,任意一个 \(1\) 或 \(0\) 都有可能导致历史最大值减去当前值变大,所以对于 \(j \ge r - l + 1\) 的 \(dp_{l - 1, \, j}\) 转移到 \(dp_{r}\) 的某一位置时不会经过 \(j = 0\),所以其转移可以视为
卷完之后得到的值具有偏移量 \(\Delta\),\(\Delta\) 是区间内 \(0\) 的数量,因为 \(0\) 贡献了 \(x^{-1}\),然后把这一段的值放到 \(dp_{r}\) 上,那对于 \(j < r - l + 1\) 的部分呢?我们利用分治,如果我们需要从 \(dp_{l - 1}\) 转移到 \(dp_{r}\),递归处理 \(dp_{l - 1} \to dp_{mid}\),\(dp_{mid} \to dp_{r}\) 即可,由于分治之后区间长度会变小,所以我们卷积无法处理的部分更少,到底层 \(l = r\) 的时候,我们就可以直接暴力转移了。
本题在时间限制上较为严格,我们需要时刻注意我们的开销,下面是几个较强的剪枝。
- 在段长为 \(16\) 的时候直接暴力,因为此时我们的卷积的开销超过了暴力的开销。
- 对数组有效值(历史最值减当前值的最大值)进行划分,如果其有效值个数不足其所覆盖的区间,说明对所有 \(j\) 都满足 \(j < r - l + 1\),我们走卷积就是浪费时间。
- 在算出递归值后,我们需要限制数组有效值的长度,因为卷到 \(r\) 的时候,我们需要的是 \(dp_{r, \, 0}\) 的值,若 \(dp_{r}\) 的有效值个数比后面的 \(1\) 的数量还多,我们就不要保存后面的数字了,因为怎么转移都不会对我们的 \(dp_{i, \, 0}\) 产生影响了。
经过上述剪枝后,得以通过,复杂度 \(O(n \log^2{n})\)。
参考代码(1250ms)
#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 _G = 3, _i = 86583718, mod = 998244353;
const int N = 6e5 + 10;
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), invi = qpow(_i), inv2 = qpow(2);
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 add(int &x, int y) {
x += (x + y < mod ? y : y - mod);
}
int n, a[N], zero[N], F[N << 1], G[N << 1];
int frac[N], inv[N], pow2[N], ans;
vector<int> dp = {1};
string s;
int C(int n, int m) {
if (n < m) return 0;
return 1ll * frac[n] * inv[n - m] % mod * inv[m] % mod;
}
void calc(int l, int r, vector<int> &dp);
vector<int> nxt(int l, int r, vector<int> &dp) {
if (dp.size() <= r - l + 1) return calc(l, r, dp), dp;
vector<int> res = dp; res.resize(r - l + 1);
calc(l, r, res);
for (int i = r - l + 1; i < dp.size(); i ++ ) F[i - (r - l + 1)] = dp[i];
for (int i = 0; i <= r - l + 1; i ++ ) G[i] = C(r - l + 1, i);
covolution(F, G, dp.size() + 1 >> 1, dp.size()); res.resize(dp.size() + zero[r] - zero[l - 1]);
for (int i = 0; i < dp.size(); i ++ ) add(res[i + zero[r] - zero[l - 1]], F[i]);
clr(F, dp.size()); clr(G, r - l + 2);
if (res.size() > n - r - (zero[n] - zero[r])) res.resize(n - r - (zero[n] - zero[r]) + 1);
return res;
}
void calc(int l, int r, vector<int> &dp) {
if (r - l <= 16) {
for (int i = l; i <= r; i ++ ) {
if (!a[i]) {
add(ans, 1ll * pow2[n - i] * dp[0] % mod);
add(dp[0], dp[0]);
for (int i = 1; i < dp.size(); i ++ ) add(dp[i - 1], dp[i]);
} else {
dp.pb(0);
for (int i = dp.size() - 1; i; i -- ) add(dp[i], dp[i - 1]);
}
}
return;
}
int mid = l + r >> 1;
dp = nxt(l, mid, dp);
dp = nxt(mid + 1, r, dp);
}
void solve() {
cin >> n >> s;
for (int i = 0; i < n; i ++ ) a[i + 1] = '1' - s[i];
for (int i = 1; i <= n; i ++ ) zero[i] = zero[i - 1] + a[i];
frac[0] = inv[0] = pow2[0] = 1;
for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod, pow2[i] = (pow2[i - 1] << 1) % mod;
inv[n] = qpow(frac[n]);
for (int i = n - 1; i; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
calc(1, n, dp);
cout << ans * qpow(2, mod - n - 1) % 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;
}

浙公网安备 33010602011771号