Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fps #92

Merged
merged 14 commits into from
Feb 6, 2024
Merged

fps #92

Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .verify-helper/timestamps.remote.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,26 @@
"test/yosupo-enumerate-palindromes.test.cpp": "2023-05-31 16:27:00 +0900",
"test/yosupo-enumerate-quotients.test.cpp": "2023-05-03 12:22:21 +0900",
"test/yosupo-enumerate-quotients.test.py": "2023-05-23 13:25:17 +0900",
"test/yosupo-exp-of-fps.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-inv-of-fps.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-lca.1.test.cpp": "2023-06-16 15:41:49 +0900",
"test/yosupo-lca.2.test.cpp": "2023-08-01 18:34:30 +0900",
"test/yosupo-line-add-get-min.test.cpp": "2023-06-19 07:11:52 +0900",
"test/yosupo-log-of-fps.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-partition-function_1.test.cpp": "2023-10-11 20:31:23 +0900",
"test/yosupo-partition-function_2.test.cpp": "2023-10-11 20:31:23 +0900",
"test/yosupo-point-add-rectangle-sum.test.cpp": "2023-06-25 14:14:02 +0900",
"test/yosupo-point-set-range-composite.1.test.cpp": "2023-08-29 23:08:45 +0900",
"test/yosupo-point-set-range-composite.2.test.cpp": "2023-08-29 23:08:45 +0900",
"test/yosupo-point-set-range-composite.test.py": "2023-09-07 14:26:13 +0900",
"test/yosupo-polynomial-taylor-shift.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-pow-of-fps.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-range-affine-range-sum.1.test.cpp": "2023-08-29 23:08:45 +0900",
"test/yosupo-range-affine-range-sum.2.test.cpp": "2023-08-29 23:08:45 +0900",
"test/yosupo-range-chmin-chmax-add-range-sum.1.test.cpp": "2023-08-01 17:59:54 +0900",
"test/yosupo-range-chmin-chmax-add-range-sum.2.test.cpp": "2023-08-01 17:59:54 +0900",
"test/yosupo-segment-add-get-min.test.cpp": "2023-06-19 08:48:44 +0900",
"test/yosupo-sharp-p-subset-sum.test.cpp": "2023-10-11 19:16:03 +0900",
"test/yosupo-shortest-path.test.cpp": "2023-06-06 14:52:29 +0900",
"test/yosupo-shortest-path.test.py": "2023-05-26 10:38:14 +0900",
"test/yosupo-unionfind.test.cpp": "2023-04-28 12:54:27 +0900",
Expand Down
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"files.associations": {
"array": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"string_view": "cpp",
"initializer_list": "cpp",
"valarray": "cpp"
}
}
225 changes: 225 additions & 0 deletions cpp/fps.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#pragma once

#include <algorithm>
#include "number-theory.hpp"

template <typename mint> struct FPS : std::vector<mint> {
using std::vector<mint>::vector;

// constructor
FPS(const std::vector<mint>& r) : std::vector<mint>(r) {}

// core operator
inline FPS pre(int siz) const {
return FPS(begin(*this), begin(*this) + std::min((int)this->size(), siz));
}
inline FPS rev() const {
FPS res = *this;
reverse(begin(res), end(res));
return res;
}
inline FPS& normalize() {
while (!this->empty() && this->back() == 0) this->pop_back();
return *this;
}

// basic operator
inline FPS operator - () const noexcept {
FPS res = (*this);
for (int i = 0; i < (int)res.size(); ++i) res[i] = -res[i];
return res;
}
inline FPS operator + (const mint& v) const { return FPS(*this) += v; }
inline FPS operator + (const FPS& r) const { return FPS(*this) += r; }
inline FPS operator - (const mint& v) const { return FPS(*this) -= v; }
inline FPS operator - (const FPS& r) const { return FPS(*this) -= r; }
inline FPS operator * (const mint& v) const { return FPS(*this) *= v; }
inline FPS operator * (const FPS& r) const { return FPS(*this) *= r; }
inline FPS operator / (const mint& v) const { return FPS(*this) /= v; }
inline FPS operator << (int x) const { return FPS(*this) <<= x; }
inline FPS operator >> (int x) const { return FPS(*this) >>= x; }
inline FPS& operator += (const mint& v) {
if (this->empty()) this->resize(1);
(*this)[0] += v;
return *this;
}
inline FPS& operator += (const FPS& r) {
if (r.size() > this->size()) this->resize(r.size());
for (int i = 0; i < (int)r.size(); ++i) (*this)[i] += r[i];
return this->normalize();
}
inline FPS& operator -= (const mint& v) {
if (this->empty()) this->resize(1);
(*this)[0] -= v;
return *this;
}
inline FPS& operator -= (const FPS& r) {
if (r.size() > this->size()) this->resize(r.size());
for (int i = 0; i < (int)r.size(); ++i) (*this)[i] -= r[i];
return this->normalize();
}
inline FPS& operator *= (const mint& v) {
for (int i = 0; i < (int)this->size(); ++i) (*this)[i] *= v;
return *this;
}
inline FPS& operator *= (const FPS& r) {
return *this = convolution((*this), r);
}
inline FPS& operator /= (const mint& v) {
assert(v != 0);
mint iv = v.inv();
for (int i = 0; i < (int)this->size(); ++i) (*this)[i] *= iv;
return *this;
}
inline FPS& operator <<= (int x) {
FPS res(x, 0);
res.insert(res.end(), begin(*this), end(*this));
return *this = res;
}
inline FPS& operator >>= (int x) {
FPS res;
res.insert(res.end(), begin(*this) + x, end(*this));
return *this = res;
}
inline mint eval(const mint& v){
mint res = 0;
for (int i = (int)this->size()-1; i >= 0; --i) {
res *= v;
res += (*this)[i];
}
return res;
}
inline friend FPS gcd(const FPS& f, const FPS& g) {
if (g.empty()) return f;
return gcd(g, f % g);
}

// advanced operation
// df/dx
inline friend FPS diff(const FPS& f) {
int n = (int)f.size();
FPS res(n-1);
for (int i = 1; i < n; ++i) res[i-1] = f[i] * i;
return res;
}

// \int f dx
inline friend FPS intg(const FPS& f) {
int n = (int)f.size();
FPS res(n+1, 0);
for (int i = 0; i < n; ++i) res[i+1] = f[i] / (i+1);
return res;
}

// inv(f), f[0] must not be 0
inline friend FPS inv(const FPS& f, int deg) {
assert(f[0] != 0);
if (deg < 0) deg = (int)f.size();
FPS res({mint(1) / f[0]});
for (int i = 1; i < deg; i <<= 1) {
res = (res + res - res * res * f.pre(i << 1)).pre(i << 1);
}
res.resize(deg);
return res;
}
inline friend FPS inv(const FPS& f) {
return inv(f, f.size());
}

// division, r must be normalized (r.back() must not be 0)
inline FPS& operator /= (const FPS& r) {
assert(!r.empty());
assert(r.back() != 0);
this->normalize();
if (this->size() < r.size()) {
this->clear();
return *this;
}
int need = (int)this->size() - (int)r.size() + 1;
*this = ((*this).rev().pre(need) * inv(r.rev(), need)).pre(need).rev();
return *this;
}
inline FPS& operator %= (const FPS &r) {
assert(!r.empty());
assert(r.back() != 0);
this->normalize();
FPS q = (*this) / r;
return *this -= q * r;
}
inline FPS operator / (const FPS& r) const { return FPS(*this) /= r; }
inline FPS operator % (const FPS& r) const { return FPS(*this) %= r; }

// log(f) = \int f'/f dx, f[0] must be 1
inline friend FPS log(const FPS& f, int deg) {
assert(f[0] == 1);
FPS res = intg(diff(f) * inv(f, deg));
res.resize(deg);
return res;
}
inline friend FPS log(const FPS& f) {
return log(f, f.size());
}

// exp(f), f[0] must be 0
inline friend FPS exp(const FPS& f, int deg) {
assert(f[0] == 0);
FPS res(1, 1);
for (int i = 1; i < deg; i <<= 1) {
res = res * (f.pre(i<<1) - log(res, i<<1) + 1).pre(i<<1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ、drkenさんのライブラリでそうなってたのの引用かと思うんですが、
res * ...をしてからの.pre(i<<1)にしたら早そうで、(というか...の計算結果はi<<1項なので.pre(i<<1)の意味がなさそう)それじゃだめなんですかね?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

両方実行してみます

Copy link
Contributor Author

@shinchankosen shinchankosen Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nyaanさんの方にはexpがみあたらなかったです。https://ei1333.github.io/library/math/fps/formal-power-series.hpp
 うしさんのを見るとけんちょんさんのと同じことをしているっぽいのでそのままにします。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いえ、うしさんは

res = res * (f.pre(i<<1) - log(res, i<<1) + 1).pre(i<<1);

ではなく

res = (res * (f.pre(i<<1) - log(res, i<<1) + 1)).pre(i<<1);

です

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@KowerKoint KowerKoint Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • (f.pre(i<<1) - log(res, i<<1) + 1)はすでにi<<1項なので(f.pre(i<<1) - log(res, i<<1) + 1).pre(i<<1)は意味がない
  • res * (f.pre(i<<1) - log(res, i<<1) + 1).pre(i<<1)は掛け算の結果項の数が多くなってるがほしいのはmod $x^{2^i}$の値だけなのでこの結果には.pre(i<<1)をつけるのが正しい

という主張でした

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

時間的には全部変わらないですね。どうしましょう?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

時間が変わらないのは意外ですが、任せます
(個人的には変なので変えたい)

}
res.resize(deg);
return res;
}
inline friend FPS exp(const FPS& f) {
return exp(f, f.size());
}

// pow(f) = exp(e * log f)
inline friend FPS pow(const FPS& f, long long e, int deg) {
if(e == 0) {
auto ret = FPS(deg, 0);
ret[0] = 1;
return ret;
}
long long i = 0;
while (i < (int)f.size() && f[i] == 0) ++i;
if (i == (int)f.size()) return FPS(deg, 0);
if ((i >= 1 and e >= deg) or i * e >= deg) return FPS(deg, 0);
mint k = f[i];
FPS res = exp(log((f >> i) / k, deg) * e, deg) * k.pow(e) << (e * i);
res.resize(deg);
return res;
}
inline friend FPS pow(const FPS& f, long long e) {
return pow(f, e, f.size());
}

inline friend FPS taylor_shift(FPS f, mint a) {
int n = f.size();
std::vector<mint> fac(n, 1), inv(n, 1), finv(n, 1);
int mod = mint::mod();
for(int i = 2; i < n; i ++) {
fac[i] = fac[i - 1] * i;
inv[i] = -inv[mod % i] * (mod / i);
finv[i] = finv[i - 1] * inv[i];
}
for(int i = 0; i < n; i ++) f[i] *= fac[i];
std::reverse(f.begin(), f.end());
FPS<mint> g(n, 1);
for(int i = 1; i < n; i ++) g[i] = g[i - 1] * a * inv[i];
f = (f * g).pre(n);
std::reverse(f.begin(), f.end());
for(int i = 0; i < n; i ++) f[i] *= finv[i];
return f;
}
};

template <typename mint> FPS<mint> modpow(const FPS<mint> &f, long long n, const FPS<mint> &m) {
if (n == 0) return FPS<mint>(1, 1);
auto t = modpow(f, n / 2, m);
t = (t * t) % m;
if (n & 1) t = (t * f) % m;
auto q = t / m;
auto r = t % m;
KowerKoint marked this conversation as resolved.
Show resolved Hide resolved
return t;
}
16 changes: 16 additions & 0 deletions test/yosupo-exp-of-fps.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://judge.yosupo.jp/problem/exp_of_formal_power_series"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n;
std::cin >> n;
FPS<mint> a(n);
for(auto& x : a) std::cin >> x;
auto f = exp(a);
for(auto e : f) std::cout << e << " ";
std::cout << std::endl;
return 0;
}
16 changes: 16 additions & 0 deletions test/yosupo-inv-of-fps.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://judge.yosupo.jp/problem/inv_of_formal_power_series"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n;
std::cin >> n;
FPS<mint> a(n);
for(auto& x : a) std::cin >> x;
auto f = inv(a);
for(auto e : f) std::cout << e << " ";
std::cout << std::endl;
return 0;
}
16 changes: 16 additions & 0 deletions test/yosupo-log-of-fps.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://judge.yosupo.jp/problem/log_of_formal_power_series"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n;
std::cin >> n;
FPS<mint> a(n);
for(auto& x : a) std::cin >> x;
auto f = log(a);
for(auto e : f) std::cout << e << " ";
std::cout << std::endl;
return 0;
}
19 changes: 19 additions & 0 deletions test/yosupo-partition-function_1.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#define PROBLEM "https://judge.yosupo.jp/problem/partition_function"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n;
std::cin >> n;
FPS<mint> f(n + 1, 0);
for(int i = 1; i <= n; i ++) {
mint inv = mint(i).inv();
for(int j = i; j <= n; j += i) f[j] += inv;
}
f = exp(f);
for(auto x : f) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
23 changes: 23 additions & 0 deletions test/yosupo-partition-function_2.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#define PROBLEM "https://judge.yosupo.jp/problem/partition_function"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n;
std::cin >> n;
FPS<mint> f(n + 1, 0);
for(int i = 0;; i ++) {
if(i * (3 * i - 1) > n * 2) break;
f[i * (3 * i - 1) / 2] = (i % 2 ? -1 : 1);
}
for(int i = -1;; i --) {
if(i * (3 * i - 1) > n * 2) break;
f[i * (3 * i - 1) / 2] = (i % 2 ? -1 : 1);
}
f = inv(f);
for(auto x : f) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
16 changes: 16 additions & 0 deletions test/yosupo-polynomial-taylor-shift.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://judge.yosupo.jp/problem/polynomial_taylor_shift"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n, c;
std::cin >> n >> c;
FPS<mint> a(n);
for(auto& x : a) std::cin >> x;
auto f = taylor_shift(a, c);
for(auto e : f) std::cout << e << " ";
std::cout << std::endl;
return 0;
}
16 changes: 16 additions & 0 deletions test/yosupo-pow-of-fps.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://judge.yosupo.jp/problem/pow_of_formal_power_series"

#include "../cpp/fps.hpp"

using mint = modint998244353;

int main() {
long long n, m;
std::cin >> n >> m;
FPS<mint> a(n);
for(auto& x : a) std::cin >> x;
auto f = pow(a, m);
for(auto e : f) std::cout << e << " ";
std::cout << std::endl;
return 0;
}
Loading