Skip to content

Commit 6cc22fc

Browse files
authored
Merge pull request #417 from hitonanode/sparse-fps
sparse fps
2 parents b2575fe + bd5b080 commit 6cc22fc

11 files changed

Lines changed: 374 additions & 97 deletions

formal_power_series/pow_of_sparse_fps.hpp

Lines changed: 0 additions & 47 deletions
This file was deleted.

formal_power_series/pow_of_sparse_fps.md

Lines changed: 0 additions & 43 deletions
This file was deleted.

formal_power_series/sparse_fps.hpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#pragma once
2+
#include <algorithm>
3+
#include <cassert>
4+
#include <concepts>
5+
#include <optional>
6+
#include <utility>
7+
#include <vector>
8+
9+
namespace sparse_fps {
10+
// https://github.com/yosupo06/library-checker-problems/issues/767#issuecomment-1166414020
11+
12+
// Calculate f(x)^k up to x^max_deg
13+
template <typename Vec>
14+
requires std::derived_from<Vec, std::vector<typename Vec::value_type>>
15+
Vec pow(const Vec &f, int max_deg, long long k) {
16+
using T = typename Vec::value_type;
17+
assert(k >= 0);
18+
19+
Vec ret(max_deg + 1);
20+
21+
if (k == 0) {
22+
ret[0] = T{1};
23+
return ret;
24+
}
25+
26+
std::vector<std::pair<int, T>> terms;
27+
int d0 = 0;
28+
while (d0 < int(f.size()) and d0 <= max_deg and f[d0] == T()) ++d0;
29+
if (d0 == int(f.size()) or d0 > max_deg) return ret;
30+
31+
if (d0 and max_deg / d0 < k) return ret;
32+
33+
for (int d = d0 + 1; d < std::min<int>(max_deg + 1, f.size()); ++d) {
34+
if (f[d] != T{}) terms.emplace_back(d - d0, f[d]);
35+
}
36+
37+
const int bias = d0 * k;
38+
ret[bias] = f[d0].pow(k);
39+
const T fd0inv = 1 / f[d0];
40+
for (int d = 0; bias + d + 1 < int(ret.size()); ++d) {
41+
// Compare [x^d](k f'g - fg')
42+
T tmp{0};
43+
for (auto [i, fi] : terms) {
44+
int j = d - i;
45+
if (0 <= j) tmp -= fi * ret[bias + j + 1] * (j + 1);
46+
j = d - (i - 1);
47+
if (0 <= j) tmp += fi * i * ret[bias + j] * T(k);
48+
}
49+
ret[bias + d + 1] = tmp * fd0inv / (d + 1);
50+
}
51+
52+
return ret;
53+
}
54+
55+
template <typename Vec>
56+
requires std::derived_from<Vec, std::vector<typename Vec::value_type>>
57+
Vec inv(const Vec &f, int max_deg) {
58+
using T = typename Vec::value_type;
59+
assert(!f.empty() and f[0] != T{0});
60+
61+
Vec ret(max_deg + 1);
62+
63+
std::vector<std::pair<int, T>> terms;
64+
for (int d = 1; d < (int)f.size() and d <= max_deg; ++d) {
65+
if (f[d] != T{}) terms.emplace_back(d, f[d]);
66+
}
67+
68+
const T f0inv = f[0].inv();
69+
ret[0] = f0inv;
70+
71+
for (int d = 1; d <= max_deg; ++d) {
72+
T tmp{0};
73+
for (auto [i, fi] : terms) {
74+
if (i > d) break;
75+
tmp -= fi * ret[d - i];
76+
}
77+
ret[d] = tmp * f0inv;
78+
}
79+
80+
return ret;
81+
}
82+
83+
template <typename Vec>
84+
requires std::derived_from<Vec, std::vector<typename Vec::value_type>>
85+
Vec log(const Vec &f, int max_deg) {
86+
using T = typename Vec::value_type;
87+
assert(!f.empty() and f[0] != T{0});
88+
89+
const auto inv = sparse_fps::inv(f, max_deg);
90+
91+
std::vector<std::pair<int, T>> df_terms;
92+
for (int d = 1; d < (int)f.size() and d <= max_deg; ++d) {
93+
if (f[d] != T{}) df_terms.emplace_back(d - 1, f[d] * T{d});
94+
}
95+
96+
Vec ret(max_deg + 1);
97+
98+
for (int d = 0; d < max_deg; ++d) {
99+
for (auto [i, fi] : df_terms) {
100+
const int j = d + i + 1;
101+
if (j > max_deg) break;
102+
ret[j] += inv[d] * fi * T{j}.inv();
103+
}
104+
}
105+
106+
return ret;
107+
}
108+
109+
template <typename Vec>
110+
requires std::derived_from<Vec, std::vector<typename Vec::value_type>>
111+
Vec exp(const Vec &f, int max_deg) {
112+
using T = typename Vec::value_type;
113+
assert(f.empty() or f[0] == T{0});
114+
115+
std::vector<std::pair<int, T>> df_terms;
116+
for (int d = 1; d < (int)f.size() and d <= max_deg; ++d) {
117+
if (f[d] != T{}) df_terms.emplace_back(d - 1, f[d] * T{d});
118+
}
119+
120+
Vec ret(max_deg + 1);
121+
ret[0] = T{1};
122+
// F' = F * f'
123+
for (int d = 1; d <= max_deg; ++d) {
124+
T tmp{0};
125+
for (auto [i, dfi] : df_terms) {
126+
if (i > d - 1) break;
127+
tmp += dfi * ret[d - 1 - i];
128+
}
129+
ret[d] = tmp * T{d}.inv();
130+
}
131+
132+
return ret;
133+
}
134+
135+
template <typename Vec>
136+
requires std::derived_from<Vec, std::vector<typename Vec::value_type>>
137+
std::optional<Vec> sqrt(const Vec &f, int max_deg) {
138+
using T = typename Vec::value_type;
139+
140+
Vec ret(max_deg + 1);
141+
142+
int d0 = 0;
143+
while (d0 < int(f.size()) and d0 <= max_deg and f[d0] == T{}) ++d0;
144+
if (d0 == int(f.size()) or d0 > max_deg) return ret;
145+
if (d0 & 1) return std::nullopt;
146+
147+
const T sqrtf0 = f[d0].sqrt();
148+
if (sqrtf0 == T{}) return std::nullopt;
149+
150+
std::vector<std::pair<int, T>> terms;
151+
const T fd0inv = 1 / f[d0];
152+
for (int d = d0 + 1; d < std::min<int>(max_deg + 1, f.size()); ++d) {
153+
if (f[d] != T{}) terms.emplace_back(d - d0, f[d] * fd0inv);
154+
}
155+
156+
const int bias = d0 / 2;
157+
const T inv2 = T{2}.inv();
158+
ret[bias] = sqrtf0;
159+
for (int d = 0; bias + d + 1 < int(ret.size()); ++d) {
160+
T tmp{0};
161+
for (auto [i, fi] : terms) {
162+
if (i > d + 1) break;
163+
int j = d - i;
164+
if (0 <= j) tmp -= fi * ret[bias + j + 1] * (j + 1);
165+
j = d - (i - 1);
166+
if (0 <= j) tmp += fi * i * ret[bias + j] * inv2;
167+
}
168+
ret[bias + d + 1] = tmp / (d + 1);
169+
}
170+
171+
return ret;
172+
}
173+
174+
} // namespace sparse_fps

formal_power_series/sparse_fps.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
title: Sparse formal power series (疎な形式的冪級数の演算)
3+
documentation_of: ./sparse_fps.hpp
4+
---
5+
6+
疎な形式的冪級数 $f(x)$ に対して,$x^N$ までの累乗・逆元・対数・指数関数・平方根を計算する.$f(x)$ の非零項数を $K$ として,いずれも $O(NK)$ で動作する.
7+
8+
## 使用方法
9+
10+
名前空間 `sparse_fps` に以下の関数が定義されている.テンプレート引数 `Vec``std::vector<T>` を継承した型で,`T` は体の元として `+`, `-`, `*`, `/`, `pow`, `inv` 等をサポートする必要がある(典型的には ModInt).
11+
12+
### `sparse_fps::pow`
13+
14+
```cpp
15+
Vec sparse_fps::pow(const Vec &f, int max_deg, long long k);
16+
```
17+
18+
$f(x)^k \bmod x^{\mathrm{max\_deg}+1}$ を計算する.$k \ge 0$.
19+
20+
### `sparse_fps::inv`
21+
22+
```cpp
23+
Vec sparse_fps::inv(const Vec &f, int max_deg);
24+
```
25+
26+
$1 / f(x) \bmod x^{\mathrm{max\_deg}+1}$ を計算する.$f_0 \neq 0$ が必要.
27+
28+
### `sparse_fps::log`
29+
30+
```cpp
31+
Vec sparse_fps::log(const Vec &f, int max_deg);
32+
```
33+
34+
$\log f(x) \bmod x^{\mathrm{max\_deg}+1}$ を計算する.$f_0 \neq 0$ が必要.
35+
36+
### `sparse_fps::exp`
37+
38+
```cpp
39+
Vec sparse_fps::exp(const Vec &f, int max_deg);
40+
```
41+
42+
$\exp f(x) \bmod x^{\mathrm{max\_deg}+1}$ を計算する.$f_0 = 0$ が必要.
43+
44+
### `sparse_fps::sqrt`
45+
46+
```cpp
47+
std::optional<Vec> sparse_fps::sqrt(const Vec &f, int max_deg);
48+
```
49+
50+
$\sqrt{f(x)} \bmod x^{\mathrm{max\_deg}+1}$ を計算する.最小次数の非零項 $f_{d_0}$ について $d_0$ が偶数かつ $f_{d_0}$ が平方根を持つ必要がある.解が存在しない場合は `std::nullopt` を返す.
51+
52+
## 問題例
53+
54+
- [Pow of Formal Power Series (Sparse) - Library Checker](https://judge.yosupo.jp/problem/pow_of_formal_power_series_sparse)
55+
- [No.1939 Numbered Colorful Balls - yukicoder](https://yukicoder.me/problems/no/1939)
56+
- [Codeforces Round 1058 (Div. 1) E. Super-Short-Polynomial-San](https://codeforces.com/contest/2159/problem/E)
57+
- [Inv of Formal Power Series (Sparse) - Library Checker](https://judge.yosupo.jp/problem/inv_of_formal_power_series_sparse)
58+
- [Log of Formal Power Series (Sparse) - Library Checker](https://judge.yosupo.jp/problem/log_of_formal_power_series_sparse)
59+
- [Exp of Formal Power Series (Sparse) - Library Checker](https://judge.yosupo.jp/problem/exp_of_formal_power_series_sparse)
60+
- [Sqrt of Formal Power Series (Sparse) - Library Checker](https://judge.yosupo.jp/problem/sqrt_of_formal_power_series_sparse)
61+
62+
## リンク
63+
64+
- [A problem collection of ODE and differential technique - Codeforces](https://codeforces.com/blog/entry/76447)
65+
- [library-checker-problems #767 (comment)](https://github.com/yosupo06/library-checker-problems/issues/767#issuecomment-1166414020)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/exp_of_formal_power_series_sparse"
2+
#include "../sparse_fps.hpp"
3+
#include "../../modint.hpp"
4+
#include <iostream>
5+
#include <vector>
6+
using namespace std;
7+
using mint = ModInt<998244353>;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
int N, K;
12+
cin >> N >> K;
13+
vector<mint> f(N);
14+
while (K--) {
15+
int i, a;
16+
cin >> i >> a;
17+
f.at(i) = a;
18+
}
19+
20+
const auto ret = sparse_fps::exp(f, N - 1);
21+
22+
for (auto e : ret) cout << e << ' ';
23+
cout << '\n';
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/inv_of_formal_power_series_sparse"
2+
#include "../sparse_fps.hpp"
3+
#include "../../modint.hpp"
4+
#include <iostream>
5+
#include <vector>
6+
using namespace std;
7+
using mint = ModInt<998244353>;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
int N, K;
12+
cin >> N >> K;
13+
vector<mint> f(N);
14+
while (K--) {
15+
int i, a;
16+
cin >> i >> a;
17+
f.at(i) = a;
18+
}
19+
20+
const auto ret = sparse_fps::inv(f, N - 1);
21+
22+
for (auto e : ret) cout << e << ' ';
23+
cout << '\n';
24+
}

0 commit comments

Comments
 (0)