Skip to content

Commit 0b8ecf0

Browse files
committed
add
1 parent bdf37ea commit 0b8ecf0

18 files changed

+556
-33
lines changed

docs/fps/count-subset-sum.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
整数列 $A=(A_0,A_1,\dots,A_{M-1})$ が与えられる.
2+
$k=0,1,\dots,N$ に対して $\sum_{i\in I}A_i=k$ を満たす $I\subseteq\{0,1,\dots,M-1\}$ の個数を $O(N\log N)$ 時間で列挙する.
3+
4+
## アルゴリズム
5+
6+
$\prod_{i}(1+x^{A_i})$ を $N$ 次まで計算できればよい.
7+
以下のように変形する.
8+
9+
$$\prod_{i}(1+x^{A_i})=\exp\left(\log\prod_{i}(1+x^{A_i})\right)=\exp\left(\sum_{i}\log(1+x^{A_i})\right)$$
10+
11+
$\log(1+x^{A_i})$ は $A_i$ の倍数次の項だけをもつので,$\exp$ の中身は調和級数の議論で $O(N\log N)$ 時間で計算できる.

docs/fps/polynomial-gcd.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
多項式 $f(x),g(x)$ に対する $\gcd(f(x),g(x))$ を $n=\max(\deg f,\deg g)$ として $O(n(\log n)^2)$ 時間で計算する.
2+
3+
また $f(x)h(x)\equiv 1\pmod{g(x)}$ となる多項式 $h(x)$ も同じ計算量で求められる.
4+
5+
## Half-GCD 法
6+
7+
Euclid の互除法をそのまま適用すると,一回の反復で次数が $1$ しか減少しない場合に $\Omega(n^2\log n)$ 時間かかる.
8+
解決するのが Half-GCD 法.
9+
10+
- [https://codeforces.com/blog/entry/101850]
11+
- [https://dl.acm.org/doi/10.1145/800125.804045]
12+
13+
$f,g$ の GCD は互除法で求められる.
14+
15+
- $f_0=f,f_1=g$ とする($\deg f\geq\deg g$).
16+
- $i=1,2,\dots$ について順に $f_{i-1}$ を $f_i$ で割った商を $q_i$ とし,$f_{i+1}=f_i-f_{i+1}q_i$ と定めていく.
17+
- $f_{i+1}=0$ となったとき $\operatorname{GCD}(f,g)=f_i$ である.
18+
19+
過程は行列で表示できる:$M_i=\begin{pmatrix}0&1\\1&-q_i\end{pmatrix}$ とすると $\begin{pmatrix}f_i\\f_{i+1}\end{pmatrix}=M_i\cdots M_1\begin{pmatrix}f_0\\f_1\end{pmatrix}$ である.
20+
21+
$\deg f_t \gt \frac{1}{2}\deg f\geq \deg f_{t+1}$ となる $t$ について $\operatorname{HGCD}(f,g)=M_t\cdots M_1$ とおく.
22+
23+
HGCD は再帰的に計算できる(証明は後述).ただし $\deg f\geq\deg g$ とし,`f >> k` は $f\operatorname{div}x^k$ を意味する.
24+
25+
```
26+
def HGCD(f, g):
27+
if deg(g) <= deg(f) / 2: return I
28+
k = floor(deg(f) / 2) + 1
29+
M = HGCD(f >> k, g >> k)
30+
(f, g)^T = M * (f, g)^T
31+
if deg(g) <= deg(f) / 2: return M
32+
(通常の互除法の 1 step を行う)
33+
if deg(g) <= deg(f) / 2: return M
34+
l = 2 * k - deg(f)
35+
return HGCD(f >> l, g >> l) * M
36+
```
37+
38+
2 回の HGCD ではどちらも次数が半分以下になっている.
39+
また多項式除算を 1 回行っている.
40+
従って $T(n)=2T(n/2)+n\log n$ を解いて $T(n)=n(\log n)^2$ であるから計算量は $O(n(\log n)^2)$ 時間である.
41+
42+
43+
HGCD を用いれば以下のアルゴリズムで GCD が求められる.
44+
ループごとに $f$ の次数が半減するので $T(n)+T(n/2)+T(n/4)+\cdots=O(n(\log n)^2)$ 時間.
45+
46+
```
47+
def GCD(f, g):
48+
while g != 0:
49+
(f, g) = HGCD(f, g) * (f, g)
50+
if g == 0: break
51+
(f, g) = (g, f % g)
52+
return f
53+
```
54+
55+
56+
### 正当性
57+
58+
形式的 Laurent 級数(有限個の負冪の項を許す)として考えると,$f$ を $g$ で割った商 $q$ は
59+
60+
$$q(x^{-1})=\frac{f(x^{-1})}{g(x^{-1})}\bmod x$$
61+
62+
と表せる.
63+
64+
65+
66+
> **補題.** 多項式 $f,g\ (\deg f-\deg g=k)$ に対し $f$ を $g$ で割った商 $q$ は $f,g$ のそれぞれ上から $k+1$ 次の係数にのみ依存する(特に $\deg f$ の値によらない).
67+
68+
<details>
69+
<summary>証明</summary>
70+
71+
両辺に $x^k=x^{\deg f-\deg g}$ をかけると
72+
$$\begin{align*}
73+
x^kq(x^{-1})
74+
&=\frac{x^{\deg f}f(x^{-1})}{x^{\deg g}g(x^{-1})}\bmod x^{k+1}\\
75+
&=\frac{x^{\deg f}f(x^{-1})\bmod x^{k+1}}{x^{\deg g}g(x^{-1})\bmod x^{k+1}}\bmod x^{k+1}
76+
\end{align*}$$
77+
であるのでよい.
78+
(証明終)
79+
80+
</details>
81+
82+
---
83+
84+
> **定理.** $(f,g)$ に対し互除法を適用して得られる行列を $M_1,M_2,\dots$ とする.非負整数 $k$ に対し,ある $t$ が存在して $\operatorname{HGCD}(f\operatorname{div}x^k,g\operatorname{div}x^k)=M_t\cdots M_1$ となる.
85+
86+
<details>
87+
<summary>証明</summary>
88+
89+
$n=\deg f$ とする.
90+
91+
HGCD 側は $\deg f'_i\gt\frac{1}{2}(n-k)$ の間操作を行う.
92+
これをみたす $i$ に対して以下を示すことができればよい.
93+
- $\deg f_i=\deg f'_i+k$
94+
- $f_i$ と $f'_i$ のそれぞれ上 $\deg f_{i-1}-\lfloor\frac{n+k}{2}\rfloor+1=\deg f'_{i-1}-\lfloor\frac{n-k}{2}\rfloor+1$ 次が一致する
95+
96+
(TODO: 証明する,実験では成り立ちそう & これより次数評価は緩められなさそう)
97+
(証明終)
98+
99+
</details>
Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,49 @@
1-
$n$ 次多項式 $f$ で $0\leq i\leq n$ に対し $f(x_i)=y_i$ を満たすものを $O(n(\log n)^2)$ 時間で求める.
1+
高々 $n-1$ 次の多項式 $f$ で $0\leq i\lt n$ に対し $f(x_i)=y_i$ を満たすものを $O(n(\log n)^2)$ 時間で求める.
2+
3+
また評価点が等比級数ならば $O(n\log n)$ 時間で求められる.
24

35
## アルゴリズム
46

5-
$g(x)=\prod_{i=0}^{n}(x-x_i)$ とすれば Lagrange 補間より以下が成り立つ.
7+
$g(x)=\prod_{i=0}^{n-1}(x-x_i)$ とすれば Lagrange 補間より以下が成り立つ.
68

7-
$$f(x)=\sum_{i=0}^{n}\frac{y_ig(x)}{g'(x_i)(x-x_i)}$$
9+
$$f(x)=\sum_{i=0}^{n-1}\frac{y_ig(x)}{g'(x_i)(x-x_i)}$$
810

911
以下のようにして $O(n(\log n)^2)$ 時間で計算できる.
1012

1113
- $g(x)$ は分割統治により $O(n(\log n)^2)$ 時間で計算できる.
1214
- $g'(x_i)$ は多点評価により $O(n(\log n)^2)$ 時間で列挙できる.
13-
- $\sum_{i=0}^{n}\frac{y_i}{g'(x_i)(x-x_i)}$ は有理数の和として分割統治により $O(n(\log n)^2)$ 時間で計算できる.
15+
- $\sum_{i=0}^{n-1}\frac{y_i}{g'(x_i)(x-x_i)}$ は有理数の和として分割統治により $O(n(\log n)^2)$ 時間で計算できる.
16+
17+
$g(x)$ の計算過程はそれ以降にも流用できる.
18+
19+
## 等比級数の場合
20+
21+
- [https://judge.yosupo.jp/problem/polynomial_interpolation_on_geometric_sequence]
22+
- [https://noshi91.github.io/algorithm-encyclopedia/polynomial-interpolation-geometric#noredirect]
23+
24+
$i=0,\dots,n-1$ について $f(ar^i)$ がわかっているとする.ただし $0\leq i\lt j\lt n$ ならば $ar^i\neq ar^j$.
25+
26+
$f$ は以下のように表示できる.
27+
$$f(x)=\sum_{i=0}^{n-1}y_i\prod_{j\neq i}\frac{x-ar^j}{a(r^i-r^j)}$$
28+
29+
$g(x)=x^{n-1}f(x^{-1})$ とすると $g$ は以下のように表示できる.
30+
$$g(x)=\sum_{i=0}^{n-1}y_i\prod_{j\neq i}\frac{1-ar^jx}{a(r^i-r^j)}$$
31+
32+
分母は以下のように変形できる.
33+
$$\begin{align*}
34+
\prod_{j\neq i}a(r^i-r^j)
35+
&=\prod_{j=0}^{i-1}a(r^i-r^j)\prod_{j=i+1}^{n-1}a(r^i-r^j)\\
36+
&=\prod_{j=1}^{i}r^{i-j}a(r^j-1)\prod_{j=1}^{n-1-i}ar^i(1-r^j)\\
37+
&=(-1)^{i}r^{i(i-1)/2+i(n-1-i)}\prod_{j=1}^{i}a(1-r^j)\prod_{j=1}^{n-1-i}a(1-r^j)
38+
\end{align*}$$
39+
40+
よって $w_i=y_i\prod_{j\neq i}\frac{1}{a(r^i-r^j)}$ は全ての $i$ に対し $O(n)$ 時間で求められるので以下を計算できればよい.
41+
$$g(x)=\sum_{i=0}^{n-1}w_i\prod_{j\neq i}(1-ar^jx)=\left(\prod_{i=0}^{n-1}(1-ar^ix)\right)\left(\sum_{i=0}^{n-1}\frac{w_i}{1-ar^ix}\right)$$
42+
43+
$F(x)=\prod_{i=0}^{n-1}(1-ar^ix)$ とおくと $(1-ax)F(rx)=F(x)(1-ar^nx)$ より $F_i=[x^i]F(x)$ について
44+
$$F_0=1,\quad (1-r^i)F_i=a(r^n-r^{i-1})F_{i-1}$$
45+
が成り立ち,$O(n)$ 時間で $F$ は求められる.
1446

15-
$g(x)$ の計算過程はそれ以降にも流用できる.
47+
$G(x)=\sum_{i=0}^{n-1}\frac{w_i}{1-ar^ix}$ とおくと
48+
$$G(x)=\sum_{i=0}^{n-1}\sum_{j=0}^{\infty}w_i(ar^ix)^j=\sum_{j=0}^{\infty}a^j\left(\sum_{i=0}^{n-1}w_ir^{ij}\right)x^j$$
49+
である.$G$ は $n-1$ 次まで求められればよいので,多項式 $\sum_{i=0}^{n-1}w_ix^i$ を $x=r^0,r^1,\dots,r^{n-1}$ で多点評価すればよい.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
$a$ を集合冪級数とする.
2+
$\sum_{s}w_s[x^s]a^i$ を $i=0,\dots,M-1$ について列挙する.
3+
4+
$O(N^22^N+NM)$ 時間.
5+
6+
- [https://maspypy.com/%e9%9b%86%e5%90%88%e3%81%b9%e3%81%8d%e7%b4%9a%e6%95%b0%e9%96%a2%e9%80%a3-3-%e5%a4%9a%e9%a0%85%e5%bc%8f%e3%81%a8%e3%81%ae%e5%90%88%e6%88%90%e3%81%ae%e8%bb%a2%e7%bd%ae]
7+
8+
## 導入
9+
10+
$a$ を fix したとき,合成 $f(a)$ の計算は $\sum_{i}f_i[x^s]a^i$ の $s$ についての列挙.
11+
12+
この転置問題は $\sum_{s}w_s[x^s]a^i$ の $i$ についての列挙.
13+
14+
多項式 $f$ との合成は以下の要領で計算していた.
15+
16+
- $g(x)=f(x+c)$ とする
17+
- $g$ を EGF にする
18+
- $g$ と $a-c$ を合成する
19+
20+
$g(x)=f(x+c)$ のとき $g_i=\sum_{j\geq i}\binom{j}{i}c^{j-i}f_j$ である.この転置は難しくない.
21+
22+
また $g$ を EGF にするには $x^k$ の係数を $k!$ 倍すればよいので,特に転置は同じものである.
23+
24+
$[x^0]a=0$ のときの EGF との合成の転置を考える.
25+
subset convolution $a\star b$ は $b$ を固定すれば $a$ について線形写像.
26+
その転置アルゴリズムは $a\star b=\operatorname{rev}(\operatorname{rev}(a)\star b)$ である.

fps/count-subset-sum.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
#include "modint/factorial.hpp"
3+
#include "fps/formal-power-series.hpp"
4+
5+
// #{ I | sum[i in I]a[i] = k } for k=0,1,...,n
6+
template <class mint>
7+
FormalPowerSeries<mint> CountSubsetSum(vector<int> a, int n) {
8+
using fact = Factorial<mint>;
9+
using fps = FormalPowerSeries<mint>;
10+
vector<int> c(n + 1, 0);
11+
for (auto v : a)
12+
if (0 <= v && v <= n) c[v]++;
13+
FormalPowerSeries<mint> f(n + 1, 0);
14+
for (int i = 1; i <= n; i++) {
15+
if (c[i] == 0) continue;
16+
mint s = c[i];
17+
for (int j = 1; i * j <= n; j++) {
18+
f[i * j] += s * fact::inv(j);
19+
s = -s;
20+
}
21+
}
22+
return f.exp();
23+
}
24+
/**
25+
* @brief Count Subset Sum
26+
* @docs docs/fps/count-subset-sum.md
27+
*/

fps/formal-power-series.hpp

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,37 @@ template <class mint>
44
struct FormalPowerSeries : vector<mint> {
55
using vector<mint>::vector;
66
using FPS = FormalPowerSeries;
7-
FPS &operator+=(const FPS &r) {
7+
FormalPowerSeries(const vector<mint>& r) : vector<mint>(r) {}
8+
FormalPowerSeries(vector<mint>&& r) : vector<mint>(std::move(r)) {}
9+
FPS& operator=(const vector<mint>& r) {
10+
vector<mint>::operator=(r);
11+
return *this;
12+
}
13+
FPS& operator+=(const FPS& r) {
814
if (r.size() > this->size()) this->resize(r.size());
915
for (int i = 0; i < (int)r.size(); i++) (*this)[i] += r[i];
1016
return *this;
1117
}
12-
FPS &operator+=(const mint &r) {
18+
FPS& operator+=(const mint& r) {
1319
if (this->empty()) this->resize(1);
1420
(*this)[0] += r;
1521
return *this;
1622
}
17-
FPS &operator-=(const FPS &r) {
23+
FPS& operator-=(const FPS& r) {
1824
if (r.size() > this->size()) this->resize(r.size());
1925
for (int i = 0; i < (int)r.size(); i++) (*this)[i] -= r[i];
2026
return *this;
2127
}
22-
FPS &operator-=(const mint &r) {
28+
FPS& operator-=(const mint& r) {
2329
if (this->empty()) this->resize(1);
2430
(*this)[0] -= r;
2531
return *this;
2632
}
27-
FPS &operator*=(const mint &v) {
33+
FPS& operator*=(const mint& v) {
2834
for (int k = 0; k < (int)this->size(); k++) (*this)[k] *= v;
2935
return *this;
3036
}
31-
FPS &operator/=(const FPS &r) {
37+
FPS& operator/=(const FPS& r) {
3238
if (this->size() < r.size()) {
3339
this->clear();
3440
return *this;
@@ -38,7 +44,7 @@ struct FormalPowerSeries : vector<mint> {
3844
FPS f(*this), g(r);
3945
g.shrink();
4046
mint coeff = g.at(g.size() - 1).inv();
41-
for (auto &x : g) x *= coeff;
47+
for (auto& x : g) x *= coeff;
4248
int deg = (int)f.size() - (int)g.size() + 1;
4349
int gs = g.size();
4450
FPS quo(deg);
@@ -52,19 +58,19 @@ struct FormalPowerSeries : vector<mint> {
5258
}
5359
return *this = ((*this).rev().pre(n) * r.rev().inv(n)).pre(n).rev();
5460
}
55-
FPS &operator%=(const FPS &r) {
61+
FPS& operator%=(const FPS& r) {
5662
*this -= *this / r * r;
5763
shrink();
5864
return *this;
5965
}
60-
FPS operator+(const FPS &r) const { return FPS(*this) += r; }
61-
FPS operator+(const mint &v) const { return FPS(*this) += v; }
62-
FPS operator-(const FPS &r) const { return FPS(*this) -= r; }
63-
FPS operator-(const mint &v) const { return FPS(*this) -= v; }
64-
FPS operator*(const FPS &r) const { return FPS(*this) *= r; }
65-
FPS operator*(const mint &v) const { return FPS(*this) *= v; }
66-
FPS operator/(const FPS &r) const { return FPS(*this) /= r; }
67-
FPS operator%(const FPS &r) const { return FPS(*this) %= r; }
66+
FPS operator+(const FPS& r) const { return FPS(*this) += r; }
67+
FPS operator+(const mint& v) const { return FPS(*this) += v; }
68+
FPS operator-(const FPS& r) const { return FPS(*this) -= r; }
69+
FPS operator-(const mint& v) const { return FPS(*this) -= v; }
70+
FPS operator*(const FPS& r) const { return FPS(*this) *= r; }
71+
FPS operator*(const mint& v) const { return FPS(*this) *= v; }
72+
FPS operator/(const FPS& r) const { return FPS(*this) /= r; }
73+
FPS operator%(const FPS& r) const { return FPS(*this) %= r; }
6874
FPS operator-() const {
6975
FPS ret(this->size());
7076
for (int i = 0; i < (int)this->size(); i++) ret[i] = -(*this)[i];
@@ -130,7 +136,7 @@ struct FormalPowerSeries : vector<mint> {
130136
}
131137
mint eval(mint x) const {
132138
mint r = 0, w = 1;
133-
for (auto &v : *this) r += w * v, w *= x;
139+
for (auto& v : *this) r += w * v, w *= x;
134140
return r;
135141
}
136142
FPS log(int deg = -1) const {
@@ -160,10 +166,10 @@ struct FormalPowerSeries : vector<mint> {
160166
return FPS(deg, mint(0));
161167
}
162168

163-
static void *ntt_ptr;
169+
static void* ntt_ptr;
164170
static void set_ntt();
165-
FPS &operator*=(const FPS &r);
166-
FPS middle_product(const FPS &r) const;
171+
FPS& operator*=(const FPS& r);
172+
FPS middle_product(const FPS& r) const;
167173
void ntt();
168174
void intt();
169175
void ntt_doubling();
@@ -172,4 +178,4 @@ struct FormalPowerSeries : vector<mint> {
172178
FPS exp(int deg = -1) const;
173179
};
174180
template <typename mint>
175-
void *FormalPowerSeries<mint>::ntt_ptr = nullptr;
181+
void* FormalPowerSeries<mint>::ntt_ptr = nullptr;

fps/multipoint-evaluation.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ vector<mint> MultipointEvaluationGeometric(FormalPowerSeries<mint> f, mint a, mi
4747
}
4848

4949
/**
50-
* @brief Multipoint Evaluation
50+
* @brief 多点評価
5151
* @docs docs/fps/multipoint-evaluation.md
5252
*/

0 commit comments

Comments
 (0)