Submission #7571733
Source Code Expand
#include <bits/stdc++.h>
using namespace std;
using i8 = int8_t;using i16 = int16_t;using i32 = int32_t;using i64 = int64_t;using u8 = uint8_t;using u16 = uint16_t;using u32 = uint32_t;using u64 = uint64_t;using f32 = float;using f64 = double;
using vi32 = vector<i32>;using vi64 = vector<i64>;using vvi32 = vector<vi32>;using vvi64 = vector<vi64>;using pi32 = pair<i32,i32>;using pi64 = pair<i64,i64>;
#define FOR(i,a,b) for(i64 i=(a), i##_len=(b); i<i##_len; ++i)
#define REP(i,n) FOR(i,0,n)
#define REPS(i,x) for(i64 i=1;i<=(i64)(x);i++)
#define RREPS(i,x) for(i64 i=((i64)(x));i>0;i--)
#define ALL(obj) (obj).begin(),(obj).end()
#define SZ(x) ((i32)(x).size())
#define CLR(ar,val) memset(ar, val, sizeof(ar))
#define cauto const auto&
#define pb push_back
#define mp make_pair
#define UNIQUE(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define perm(c) sort(ALL(c));for(bool c##p=1;c##p;c##p=next_permutation(all(c)))
i32 dx[4]={1,0,-1,0};
i32 dy[4]={0,1,0,-1};
const i32 INF32 = 0x3F3F3F3F;
const i64 INF64 = 0x3F3F3F3F3F3F3F3F;
template<class T> inline bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T> inline bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }
/// pair vectorに対して直接入出力できるようにする
template<typename A, typename B> istream &operator>>(istream &is, pair<A, B> &p) { is >> p.first >> p.second;return is; }
template<typename A, typename B> ostream &operator<<(ostream &os, const pair<A, B>& p) { os << p.first << ' ' << p.second;return os; }
template<typename T> istream &operator>>(istream &is, vector<T> &v) { for(T& in : v) is >> in; return is; }
template<typename T> ostream &operator<<(ostream &os, const vector<T> &v) { for(i32 i = 0; i < (i32)v.size(); i++) os << v[i] << (i+1 != (i32)v.size() ? " " : ""); return os; }
/// # べき乗
i64 mod_pow(i64 n, i64 k, i64 mod) {
i64 res = 1;
for(; k>0; k>>=1) {
if(k & 1) (res *= n) %= mod;
(n *= n) %= mod;
}
return res;
}
/// # 余りで四則演算する構造体
template<i64 mod>
struct ModInt {
i64 v;
i64 mod_pow(i64 x, i64 n) const {
return (!n) ? 1 : (mod_pow((x*x)%mod,n/2) * ((n&1)?x:1)) % mod;
}
ModInt(i64 a = 0) : v(a >= mod ? a % mod : a) {}
ModInt operator+ ( const ModInt& b ) const {
return (v + b.v >= mod ? ModInt(v + b.v - mod) : ModInt(v + b.v));
}
ModInt operator- () const {
return ModInt(-v);
}
ModInt operator- ( const ModInt& b ) const {
return (v - b.v < 0 ? ModInt(v - b.v + mod) : ModInt(v - b.v));
}
ModInt operator* ( const ModInt& b ) const {return (v * b.v) % mod;}
ModInt operator/ ( const ModInt& b ) const {return (v * mod_pow(b.v, mod-2)) % mod;}
bool operator== ( const ModInt &b ) const {return v == b.v;}
ModInt& operator+= ( const ModInt &b ) {
v += b.v;
if(v >= mod) v -= mod;
return *this;
}
ModInt& operator-= ( const ModInt &b ) {
v -= b.v;
if(v < 0) v += mod;
return *this;
}
ModInt& operator*= ( const ModInt &b ) {
(v *= b.v) %= mod;
return *this;
}
ModInt& operator/= ( const ModInt &b ) {
(v *= mod_pow(b, mod-2)) %= mod;
return *this;
}
ModInt pow(i64 x) { return ModInt(mod_pow(v, x)); }
operator i32() const { return int(v); }
operator i64() const { return v; }
};
template<i64 mod>
ostream& operator<< (ostream& out, ModInt<mod> a) {return out << a.v;}
template<i64 mod>
istream& operator>> (istream& in, ModInt<mod>& a) {
in >> a.v;
return in;
}
template <typename NumType>
struct Combination {
i32 LIMIT;
vector<NumType> fact_, finv_;
Combination() {}
Combination(i32 LIMIT_) : LIMIT(LIMIT_), fact_(LIMIT+1), finv_(LIMIT+1) {
fact_[0] = finv_[LIMIT] = NumType(1);
for(i32 i=1; i<=LIMIT; i++) {
fact_[i] = fact_[i-1] * NumType(i);
}
finv_[LIMIT] /= fact_[LIMIT];
for(i32 i=LIMIT-1; i>=0; i--) {
finv_[i] = finv_[i+1] * NumType(i+1);
}
}
inline NumType fact(i32 k) const { return fact_[k]; }
inline NumType finv(i32 k) const { return finv_[k]; }
NumType P(i32 n, i32 r) const {
if(r < 0 or n < r) return NumType(0);
return fact_[n] * finv_[n-r];
}
NumType C(i32 n, i32 r) const {
if(r < 0 or n < r) return NumType(0);
return fact_[n] * finv_[n-r] * finv_[r];
}
// 重複組み合わせ
NumType H(i32 n, i32 r) const {
if(n < 0 or r < 0) return NumType(0);
return r == 0 ? NumType(0) : C(n + r - 1, r);
}
// ベル数 (区別できる n 個のボールを区別できない k 個以下の箱に分割)
// B(n, n) := n 個のボールを任意個のグループに分割する場合の数
NumType B(i32 n, i32 k) const {
if(n == 0) return NumType(1);
k = min(n, k);
NumType ret(0);
vector<NumType> pref(k + 1); pref[0] = NumType(1);
for(i32 i=1; i<=k; i++) {
if(i & 1) pref[i] = pref[i-1] - finv_[i];
else pref[i] = pref[i-1] + finv_[i];
}
for(i32 i=1; i<=k; i++) {
ret += NumType(i).pow(n) * finv_[i] * pref[k-i];
}
return ret;
}
// スターリング数 (区別できる n 個のボールを区別できない k 個の箱に分割)
NumType S(i32 n, i32 k) const {
if(n < k) return NumType(0);
NumType ans(0);
for(i32 i=0; i<=k; i++) {
NumType val = C(k, i) * NumType(i).pow(n);
if((k - i) % 2) ans -= val;
else ans += val;
}
return ans * finv_[k];
}
};
template <typename NumType>
vector<NumType> get_inv_table(i32 N, i32 P) {
vector<NumType> res;
for(i32 i=0; i<N; i++) {
res.emplace_back(NumType(mod_pow(i, P-2, P)));
}
return res;
}
// f(0) ... f(N) の値がわかっているときに
// 多項式補間を O(N) で実現するバージョンのラグランジュ補間
template <typename NumType, i32 LIMIT = 2000010>
struct SpecificLagrangePolynomial {
Combination<NumType> comb;
vector<NumType> y, inv_table;
bool use_inv_table;
SpecificLagrangePolynomial() : comb(), y() {}
SpecificLagrangePolynomial(vector<NumType> y_, i32 P=LIMIT)
: comb(min(LIMIT, P-1)), y(y_), use_inv_table(false) {
if(P < LIMIT) use_inv_table = true;
if(use_inv_table) {
inv_table = get_inv_table<NumType>(P, P);
}
}
NumType interpolate(NumType p) {
NumType pre(1);
i32 N = y.size(), pv = i32(p);
if(pv < N) return y[pv];
for(i32 i=0; i<N; i++) {
pre *= NumType(pv - i);
}
NumType res(0);
for(i32 i=0; i<N; i++) {
NumType numer(0), denom(1);
if(use_inv_table)
numer = pre * inv_table[pv - i];
else
numer = pre / NumType(pv - i);
denom *= comb.finv(N - 1 - i) * comb.finv(i);
if((N - 1 - i) % 2) res -= numer * denom * y[i];
else res += numer * denom * y[i];
}
return res;
}
};
/// # 組み合わせ
/// nCr をmodで割った余りを求める
i64 CombinationMod(i32 n, i32 r, i32 mod=1) {
if (n < 0 || r < 0 || r > n) return -1;
if (n - r < r) r = n - r;
if (r == 0) return 1;
if (r == 1) return n;
vi64 numerator(r);
vi64 denominator(r);
for (i32 k = 0; k < r; k++) {
numerator[k] = n - r + k + 1;
denominator[k] = k + 1;
}
for (i32 p = 2; p <= r; p++) {
i32 pivot = denominator[p-1];
if (pivot > 1) {
i32 offset = (n - r) % p;
for (i32 k = p - 1; k < r; k += p) {
numerator[k-offset] /= pivot;
denominator[k] /= pivot;
}
}
}
i64 ret = 1LL;
if (mod == 1) {
for (i32 k = 0; k < r; k++) {
if (numerator[k] > 1) ret *= numerator[k];
}
} else {
for (i32 k = 0; k < r; k++) {
if (numerator[k] > 1) ret = (ret * numerator[k]) % mod;
}
}
return ret;
}
/// # 拡張ユークリッドの互除法
/// gcd(a, b) = a * x + b * yのxとyを求める
template<typename T>
T extgcd(T a, T b, T &x, T &y) {
T d = a;
if(b != 0) {
d = extgcd(b, a % b, y, x);
y -= (a / b) * x;
} else {
x = 1;
y = 0;
}
return d;
}
/// # オイラーのφ
i64 euler_phi(i64 n) {
i64 ret = n;
for (i64 i = 2; i * i <= n; i++) {
if (n % i == 0) {
ret -= ret / i;
while (n % i == 0) n /= i;
}
}
if (n > 1) ret -= ret / n;
return ret;
}
vi32 euler_phi_table(i32 n) {
vi32 euler(n + 1);
for (i32 i = 0; i <= n; i++) {
euler[i] = i;
}
for (i32 i = 2; i <= n; i++) {
if (euler[i] == i) {
for (i32 j = i; j <= n; j += i) {
euler[j] = euler[j] / i * (i - 1);
}
}
}
return euler;
}
/// # 進数変換
template<typename T>
vector<T> convert_base(T x, T b) {
vector<T> ret;
T t = 1, k = abs(b);
while(x) {
ret.emplace_back((x * t) % k);
if(ret.back() < 0) ret.back() += k;
x -= ret.back() * t;
x /= k;
t *= b / k;
}
if(ret.empty()) ret.emplace_back(0);
reverse(ALL(ret));
return ret;
}
/// # 素数テーブル
vector<bool> prime_table(i32 n) {
vector<bool> prime(n + 1, true);
if(n >= 0) prime[0] = false;
if(n >= 1) prime[1] = false;
for(i32 i = 2; i * i <= n; i++) {
if(!prime[i]) continue;
for(i32 j = i + i; j <= n; j += i) {
prime[j] = false;
}
}
return prime;
}
/// # 素数判定
bool is_prime(i64 x) {
for(i64 i = 2; i * i <= x; i++) {
if(x % i == 0) return false;
}
return true;
}
/// # 離散対数
/// mod_log(a, b, p) : a^x === b (mod p) を満たすx(>=0)の最小値 存在しない場合-1
i64 mod_log(i64 a, i64 b, i64 p) {
i64 ok = p, ng = -1;
while(ok - ng > 1) {
auto mid = (ok + ng) / 2;
if(mid * mid >= p) ok = mid;
else ng = mid;
}
unordered_map<i64, i64> baby;
baby.reserve(ok);
i64 factor = 1;
for(i64 i = 0, e = b; i < ok; i++) {
baby[e] = i;
(factor *= a) %= p;
(e *= a) %= p;
}
for(i64 i = 1, e = factor; i <= ok; i++) {
auto it = baby.find(e);
if(it != end(baby)) return i * ok - it->second;
(e *= factor) %= p;
}
return -1;
}
/// # Nの商
/// Nをi (1 <= i <= N)で割ったとき商をすべて列挙する
/// ```c++
/// auto a = quotient_range(8);
/// for (const auto& e : a) cout << e << endl;
/// /***
/// 1 1 8
/// 2 2 4
/// 3 4 2
/// 5 8 1
/// ***/
/// ```
template<typename T>
vector<pair<pair<T, T>, T>> quotient_range(T N) {
T M;
vector<pair<pair<T, T>, T>> ret;
for (M = 1; M * M <= N; M++) {
ret.emplace_back(make_pair(M, M), N / M);
}
for (T i = M; i >= 1; i--) {
T L = N / (i+1) + 1;
T R = N / i;
if (L <= R && ret.back().first.second < L) ret.emplace_back(make_pair(L, R), N / L);
}
return ret;
}
/// # 素因数分解
map<i64, i32> prime_factor(i64 n) {
map<i64, i32> ret;
for (i64 i = 2; i * i <= n; i++) {
while(n % i == 0) {
ret[i]++;
n /= i;
}
}
if (n != 1) ret[n] = 1;
return ret;
}
/// # 約数
vi64 divisor(i64 n) {
vi64 ret;
for (i64 i = 1; i * i <= n; i++) {
if (n % i == 0) {
ret.push_back(i);
if (i * i != n) ret.push_back(n / i);
}
}
/* sort(ALL(ret)); */
return ret;
}
/// # SegmentTree
/// モノイドに対する区間への演算がO(logN)でできる
///
/// ## 初期化
/// サイズnで初期化 fは2つの区間の要素をマージする演算 M1はモノイドの単位元
/// ```c++:segment-tree
/// SegmentTree(n,f,M1)
/// ```
///
/// ## 代入
/// k番目の要素にxを代入する
/// ```c++:segment-tree
/// set(k, x)
/// ```
///
/// ## 構築
/// セグメント木を構築する
/// ```c++:segment-tree
/// build()
/// ```
///
/// ## 演算
/// 区間[a,b)に二項演算した結果を返す
/// ```c++:segment-tree
/// query(a, b)
/// ```
///
/// ## 変更
/// k番目の要素をxに変更する
/// ```c++:segment-tree
/// update(k, x)
/// ```
///
/// ## 要素取得
/// k番目の要素を取得する
/// ```c++:segment-tree
/// SegmentTree<i32> seg(30, [](i32 a, i32 b) { return min(a, b); }, INT_MAX);
/// seg[10];
/// ```
template<typename Monoid>
struct SegmentTree {
using F = function<Monoid(Monoid, Monoid)>;
i32 sz;
vector<Monoid> seg;
const F f;
const Monoid M1;
SegmentTree(i32 n, const F f, const Monoid &M1) : f(f), M1(M1) {
sz = 1;
while(sz < n) sz <<= 1;
seg.assign(2 * sz, M1);
}
void set(i32 k, const Monoid &x) {
seg[k+sz] = x;
}
void build() {
for(i32 k = sz - 1; k > 0; k--) {
seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
}
}
void update(i32 k, const Monoid &x) {
k += sz;
seg[k] = x;
while(k >>= 1) {
seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
}
}
Monoid query(i32 a, i32 b) {
Monoid L = M1, R = M1;
for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
if (a & 1) L = f(L, seg[a++]);
if (b & 1) R = f(seg[--b], R);
}
return f(L, R);
}
Monoid operator[](const i32 &k) const {
return seg[k+sz];
}
};
/// # BinaryIndexedTree
/// 数列にある要素を加える操作と区間和を求める操作をO(logN)でできるデータ構造
///
/// ## 初期化
/// ```c++:BIT
/// BinaryIndexedTree(100)
/// ```
///
/// ## 閉区間[0,k]の合計
/// ```c++:BIT
/// sum(k)
/// ```
///
/// ## k番目の数列の要素にxを加える
/// ```c++:BIT
/// add(k, x)
/// ```
template<typename T>
struct BinaryIndexedTree {
vector<T> dat;
BinaryIndexedTree(i32 sz) {
dat.assign(++sz, 0);
}
T sum(i32 k) {
T ret = 0;
for(++k; k > 0; k -= k & -k) ret += dat[k];
return ret;
}
void add(i32 k, T x) {
for(++k; k < dat.size(); k += k & -k) dat[k] += x;
}
};
/// # Trie木 [wiki/トライ木](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%82%A4%E6%9C%A8)
/// あるノードの配下のすべてのノードには
/// 自身に対応する文字列に共通するプレフィックスがあり
/// ルートには空の文字列が対応している
///
/// Trie<char_size, margin>() : 文字列の種類数char_size, 開始文字がmarginのトライ木を作成する
/// add(S) : 文字列Sをトライ木に追加する
/// query(S,f) : 文字列Sのプレフィックスに一致する文字列を検索する。一致した文字列ごとに関数fが呼び出される
/// size() : ノード数を返す
/// count() : 存在する文字列の個数を返す
template<i32 char_size>
struct TrieNode {
i32 nxt[char_size];
i32 exist;
vi32 accept;
TrieNode() : exist(0) {
CLR(nxt,-1);
}
};
template<i32 char_size, i32 margin>
struct Trie {
using Node = TrieNode<char_size>;
vector<Node> nodes;
i32 root;
Trie() : root(0) {
nodes.push_back(Node());
}
void update_direct(i32 node, i32 id) {
nodes[node].accept.push_back(id);
}
void update_child(i32 node, i32 child, i32 id) {
++nodes[node].exist;
}
void add(const string& str, i32 str_index, i32 node_index, i32 id) {
if (str_index == SZ(str)) {
update_direct(node_index, id);
} else {
const i32 c = str[str_index] - margin;
if (nodes[node_index].nxt[c] == -1) {
nodes[node_index].nxt[c] = (i32)nodes.size();
nodes.push_back(Node());
}
add(str, str_index + 1, nodes[node_index].nxt[c], id);
update_child(node_index, nodes[node_index].nxt[c], id);
}
}
void add(const string &str, i32 id) {
add(str, 0, 0, id);
}
void add(const string &str) {
add(str, nodes[0].exist);
}
void query(const string &str, const function<void(i32)> &f, i32 str_index, i32 node_index) {
for (auto &idx : nodes[node_index].accept) f(idx);
if (str_index == SZ(str)) {
return;
} else {
const i32 c = str[str_index] - margin;
if (nodes[node_index].nxt[c] == -1) return;
query(str, f, str_index + 1, nodes[node_index].nxt[c]);
}
}
void query(const string &str, const function<void(i32)> &f) {
query(str, f, 0, 0);
}
i32 count() const {
return (nodes[0].exist);
}
i32 size() const {
return ((i32)nodes.size());
}
};
/// # Binary-Trie木
template<typename T>
struct BinaryTrieNode {
using Node = BinaryTrieNode<T>;
BinaryTrieNode<T> *nxt[2];
i32 max_index;
BinaryTrieNode() : max_index(-1) {
nxt[0] = nxt[1] = nullptr;
}
void update_direct(i32 id) {
max_index = max(max_index, id);
}
void update_child(Node *child, i32 id) {
max_index = max(max_index, id);
}
Node* add(const T &bit, i32 bit_index, i32 id, bool need=true) {
Node* node = need ? new Node(*this) : this;
if (bit_index == -1) {
node->update_child(id);
} else {
const i32 c = (bit >> bit_index) & 1;
if (node->nxt[c] == nullptr) node->nxt[c] = new Node(), need = false;
node->nxt[c] = node->nxt[c]->add(bit, bit_index - 1, id, need);
node->update_child(node->nxt[c], id);
}
return node;
}
inline T min_query(T bit, i32 bit_index, i32 bit2, i32 l) {
if (bit_index == -1) return bit;
i32 c = (bit2 >> bit_index) & 1;
if (nxt[c] != nullptr && l <= nxt[c]->max_index) {
return nxt[c]->min_query(bit, bit_index - 1, bit2, l);
} else {
return nxt[1^c]->min_query(bit | (1LL << bit_index), bit_index - 1, bit2, l);
}
}
};
/// # 永続Binary-Trie木
template<typename T, i32 MAX_LOG>
struct PersistentBinaryTrie {
using Node = BinaryTrieNode<T>;
Node* root;
PersistentBinaryTrie(Node* root) : root(root) { }
PersistentBinaryTrie() : root(new Node()) { }
PersistentBinaryTrie add(const T &bit, i32 id) {
return PersistentBinaryTrie(root->add(bit, MAX_LOG, id));
}
T min_query(i32 bit, i32 l) {
return root->min_query(0, MAX_LOG, bit, l);
}
};
/// # 配列のk個ずつの最小値を求める
///
/// ```c++:slide minimum
/// vector<i32> A = {0,1,2,3,2,1,2,3,4,3,2,1,0};
/// auto B = slideMin(A, 4);
/// //B == {0,1,1,1,1,1,2,2,1,0}
/// ```
template<typename T>
vector<T> slideMin(const vector<T>& v, i32 k) {
deque<i32> deq;
vector<T> ret;
for(i32 i=0;i < (i32)v.size(); i++) {
while(!deq.empty() && v[deq.back()] >= v[i]) {
deq.pop_back();
}
deq.push_back(i);
if (i - k + 1 >= 0) {
ret.emplace_back(v[deq.front()]);
if (deq.front() == i - k + 1) deq.pop_front();
}
}
return ret;
}
/// # 累積和を計算する
///
/// ## 初期化
/// ```c++
/// CumulativeSum<i64> A(sz);
/// ```
///
/// ## インデックスkに値vを加える
/// ```c++
/// A.add(k,v);
/// ```
///
/// ## 累積和を構築
/// ```c++
/// A.build();
/// ```
///
/// ## 配列を表示
/// ```c++
/// A.printf();
/// ```
///
/// ## 閉区間[0,k]の和を計算
/// ```c++
/// A.query(k);
/// ```
template<class T>
struct CumulativeSum {
vector<T> dat;
CumulativeSum(i32 sz) : dat(sz, 0) { };
void add(i32 k, T x) {
dat[k] += x;
}
void build() {
for (i32 i = 1; i < (i32)dat.size(); i++) {
dat[i] += dat[i-1];
}
}
void printf() {
for (i32 i = 0; i < (i32)dat.size(); i++) {
if (i == 0) {
cout << "[" << dat[i];
} else if (i == ((i32)dat.size() - 1)) {
cout << ", " << dat[i] << "]" << endl;
} else {
cout << ", " << dat[i];
}
}
}
T query(i64 k) {
if (k < 0) return 0;
return (dat[min(k, (i64)(dat.size() - 1))]);
}
};
/// 累積和の2次元版
template<class T>
struct CumulativeSum2D {
vector<vector<T>> dat;
CumulativeSum2D(i32 w, i32 h) : dat(w + 1, vector<T>(h + 1, 0)) { }
void add(i32 x, i32 y, T z) {
++x; ++y;
if (x >= SZ(dat) || y >= (SZ(dat[0]))) return;
dat[x][y] += z;
}
void build() {
for (i32 i = 1; i < SZ(dat); i++) {
for (i32 j = 1; j < SZ(dat[i]); j++) {
dat[i][j] += dat[i][j-1] + dat[i - 1][j] - dat[i - 1][j - 1];
}
}
}
void printf() {
for (i32 i = 0; i < SZ(dat); i++) {
for (i32 j = 0; j < SZ(dat[i]); j++) {
cout << dat[i][j] << (j == (SZ(dat[i]) - 1) ? "" : " ");
}
cout << endl;
}
}
T query(i32 sx, i32 sy, i32 gx, i32 gy) {
return (dat[gx][gy] - dat[sx][gy] - dat[gx][sy] + dat[sx][sy]);
}
};
/// # 最長増加部分列 (LIS)
/// a : 数列aの最長増加部分列の長さを求める
/// strict : trueのとき狭義単調増加列 falseのとき広義単調増加列
template<typename T>
size_t longest_increasing_subsequence(const vector<T> &a, bool strict) {
vector<T> lis;
for (auto &p : a){
typename vector<T>::iterator it;
if (strict) it = lower_bound(begin(lis), end(lis), p);
else it = upper_bound(begin(lis), end(lis), p);
if (end(lis) == it) lis.emplace_back(p);
else *it = p;
}
return lis.size();
}
/// # 個数制限付きナップザック問題 (O(NW))
/// w : i番目の重さの配列
/// m : i番目の品物をいくつまで選べるかの配列
/// v : i番目の品物の価値の配列
/// W : ナップザックの容量
/// NG: 容量として不適当な値 (-1や-INFなど、初期化のため)
template<typename T, typename Compare = greater<T>>
vector<T> knapsack_limitations(const vi32 &w, const vi32 &m, const vector<T> &v, const i32 &W, const T &NG, const Compare &comp = Compare()) {
const i32 N = (i32) w.size();
vector<T> dp(W+1, NG), deqv(W+1);
dp[0] = T();
vector<i32> deq(W+1);
for(i32 i = 0; i < N; i++) {
for (i32 a = 0; a < w[i]; a++) {
i32 s = 0, t = 0;
for (i32 j = 0; w[i] * j + a <= W; j++) {
if (dp[w[i] * j + a] != NG) {
auto val = dp[w[i] * j + a] - j * v[i];
while(s < t && comp(val, deqv[t - 1])) --t;
deq[t] = j;
deqv[t++] = val;
}
if (s < t) {
dp[j*w[i]+a] = deqv[s] + j * v[i];
if (deq[s] == j - m[i]) ++s;
}
}
}
}
return dp;
}
/// # 個数制限付きナップザック問題 (O(N^2max(vi)^2))
/// w : i番目の重さの配列
/// m : i番目の品物をいくつまで選べるかの配列
/// v : i番目の品物の価値の配列
/// W : ナップザックの容量
template<typename T>
T knapsack_limitations2(const vector<T> &w, const vector<T> &m, const vector<i32> &v, const T &W) {
const i32 N = (i32) w.size();
auto v_max = *max_element(begin(v), end(v));
if(v_max == 0) return 0;
vector<i32> ma(N);
vector<T> mb(N);
for(i32 i = 0; i < N; i++) {
ma[i] = min< T >(m[i], v_max - 1);
mb[i] = m[i] - ma[i];
}
T sum = 0;
for(i32 i = 0; i < N; i++) sum += ma[i] * v[i];
auto dp = knapsack_limitations(v, ma, w, sum, T(-1), less<>());
vector<i32> ord(N);
iota(begin(ord), end(ord), 0);
sort(begin(ord), end(ord), [&](i32 a, i32 b) {
return v[a] * w[b] > v[b] * w[a];
});
T ret = T();
for(i32 i = 0; i < SZ(dp); i++) {
if(dp[i] > W || dp[i] == -1) continue;
T rest = W - dp[i], cost = i;
for(auto &p : ord) {
auto get = min(mb[p], rest / w[p]);
if(get == 0) break;
cost += get * v[p];
rest -= get * w[p];
}
ret = max(ret, cost);
}
return ret;
}
/// # 数論変換NTT
namespace NTT {
i64 extgcd(i64 a, i64 b, i64 &x, i64 &y) {
i64 d;
return b == 0 ? (x = 1, y = 0, a) : (d = extgcd(b, a % b, y, x), y -= a / b * x, d);
}
i64 modinv(i64 a, i64 mod) {
i64 x, y;
extgcd(a, mod, x, y);
x %= mod;
return x < 0 ? x + mod : x;
}
i64 modpow(i64 a, i64 b, i64 mod) {
i64 r = 1;
a %= mod;
while(b) {
if(b & 1) r = r * a % mod;
a = a * a % mod;
b >>= 1;
}
return r;
}
template<i64 mod, i64 primitive>
struct Core {
Core() {}
using uint = uint_fast32_t;
i32 MAX_H;
vi64 zetaList, zetaInvList;
Core(i32 MAX_H) : MAX_H(MAX_H), zetaList(MAX_H), zetaInvList(MAX_H) {
assert((mod & ((1 << MAX_H) - 1)) == 1);
zetaList.back() = modpow(primitive, (mod - 1) / (1 << MAX_H), mod);
zetaInvList.back() = modinv(zetaList.back(), mod);
for(i32 ih = MAX_H - 2; ih >= 0; --ih) {
zetaList[ih] = zetaList[ih + 1] * zetaList[ih + 1] % mod;
zetaInvList[ih] = zetaInvList[ih + 1] * zetaInvList[ih + 1] % mod;
}
}
void fft(vi64 &a, uint n, uint h, bool inv) const {
static vi64 tmp;
tmp.resize(n);
uint mask = n - 1;
for(uint i = n >> 1, ih = h - 1; i >= 1; i >>= 1, --ih) {
i64 zeta = inv ? zetaInvList[h - 1 - ih] : zetaList[h - 1 - ih];
i64 powZeta = 1;
for(uint j = 0; j < n; j += i) {
for(uint k = 0; k < i; ++k) {
tmp[j + k] = (a[((j << 1) & mask) + k] + powZeta * a[(((j << 1) + i) & mask) + k]) % mod;
}
powZeta = powZeta * zeta % mod;
}
swap(a, tmp);
}
if(inv) {
i64 invN = modinv(n, mod);
for(uint i = 0; i < n; ++i) a[i] = a[i] * invN % mod;
}
}
vi64 conv(const vi64 &a, const vi64 &b) const {
assert(a.size() + b.size() - 1 <= (1 << MAX_H));
if(a.size() == 0) return {};
if(b.size() == 0) return {};
return _conv(a, b);
}
vi64 _conv(vi64 a, vi64 b) const {
uint deg = a.size() + b.size() - 1;
uint h = 0, n = 1;
while(n < deg) n <<= 1, ++h;
a.resize(n), b.resize(n);
return _convStrict(a, b, n, h);
}
vi64 _convStrict(vi64 a, vi64 b, uint n, uint h) const {
fft(a, n, h, 0), fft(b, n, h, 0);
for(uint i = 0; i < n; ++i) a[i] = a[i] * b[i] % mod;
fft(a, n, h, 1);
return a;
}
};
// NTT_PRIMES {{{
constexpr i64 NTT_PRIMES[][2] = {
{1224736769, 3}, // 2^24 * 73 + 1,
{1053818881, 7}, // 2^20 * 3 * 5 * 67 + 1
{1051721729, 6}, // 2^20 * 17 * 59 + 1
{1045430273, 3}, // 2^20 * 997 + 1
{1012924417, 5}, // 2^21 * 3 * 7 * 23 + 1
{1007681537, 3}, // 2^20 * 31^2 + 1
{1004535809, 3}, // 2^21 * 479 + 1
{998244353, 3}, // 2^23 * 7 * 17 + 1
{985661441, 3}, // 2^22 * 5 * 47 + 1
{976224257, 3}, // 2^20 * 7^2 * 19 + 1
{975175681, 17}, // 2^21 * 3 * 5 * 31 + 1
{962592769, 7}, // 2^21 * 3^3 * 17 + 1
{950009857, 7}, // 2^21 * 4 * 151 + 1
{943718401, 7}, // 2^22 * 3^2 * 5^2 + 1
{935329793, 3}, // 2^22 * 223 + 1
{924844033, 5}, // 2^21 * 3^2 * 7^2 + 1
{469762049, 3}, // 2^26 * 7 + 1
{167772161, 3}, // 2^25 * 5 + 1
};
// }}}
template <i32 I>
void conv_for(i32 MAX_H, i32 n, i32 h, const vector<i64> &a,
const vector<i64> &b, vector<i64> &mods,
vector<i64> &coeffs,
vector<vector<i64>> &constants) {
static const Core<NTT_PRIMES[I][0], NTT_PRIMES[I][1]> ntt(MAX_H);
auto c = ntt._convStrict(a, b, n, h);
mods[I] = NTT_PRIMES[I][0];
if(I >= 1) {
conv_for<I - 1>(MAX_H, n, h, a, b, mods, coeffs, constants);
}
// garner
for(size_t i = 0; i < c.size(); ++i) {
i64 v = (c[i] - constants[I][i]) * modinv(coeffs[I], mods[I]) % mods[I];
if(v < 0) v += mods[i];
for(size_t j = I + 1; j < mods.size(); ++j) {
constants[j][i] = (constants[j][i] + coeffs[j] * v) % mods[j];
}
}
for(size_t j = I + 1; j < mods.size(); ++j) {
coeffs[j] = (coeffs[j] * mods[I]) % mods[j];
}
}
template <>
void conv_for< -1 >(i32, i32, i32, const vector<i64> &, const vector<i64> &,
vector<i64> &, vector<i64> &,
vector<vector<i64>> &) {}
template <i32 USE>
vector<i64> conv(i32 MAX_H, vector<i64> a, vector<i64> b,
i64 mod) {
static_assert(USE >= 1, "USE must be positive");
static_assert(USE <= sizeof(NTT_PRIMES) / sizeof(*NTT_PRIMES), "USE is too big");
i32 deg = a.size() + b.size() - 1;
i32 n = 1, h = 0;
while(n < deg) n <<= 1, ++h;
a.resize(n), b.resize(n);
vector<i64> coeffs(USE + 1, 1);
vector<vector<i64>> constants(USE + 1, vector<i64>(n, 0));
vector<i64> mods(USE + 1, mod);
conv_for<USE - 1>(MAX_H, n, h, a, b, mods, coeffs, constants);
return constants.back();
}
} // namespace NTT
const i32 MAX_H = 18;
NTT::Core<NTT::NTT_PRIMES[0][0], NTT::NTT_PRIMES[0][1]> ntt(MAX_H);
void Main();
i32 main() {cin.tie(nullptr);ios_base::sync_with_stdio(false);cout << fixed << setprecision(15);Main();return 0;}
void Main() {
//-------------------------------------------------------------------------
i32 n;
cin >> n;
vi64 a(n), b(n);
for(i32 i = 0; i < n; ++i) cin >> a[i] >> b[i];
auto c = ntt.conv(a, b);
cout << 0 << '\n';
for(i32 i = 0; i < 2 * n - 1; ++i) cout << c[i] << '\n';
//-------------------------------------------------------------------------
}
Submission Info
Submission Time |
|
Task |
C - 高速フーリエ変換 |
User |
mizumon |
Language |
C++14 (GCC 5.4.1) |
Score |
100 |
Code Size |
31371 Byte |
Status |
AC |
Exec Time |
68 ms |
Memory |
12128 KB |
Judge Result
Set Name |
Sample |
All |
Score / Max Score |
0 / 0 |
100 / 100 |
Status |
|
|
Set Name |
Test Cases |
Sample |
00_sample_01 |
All |
00_sample_01, 01_00_01, 01_01_19, 01_02_31, 01_03_22, 01_04_31, 01_05_40, 01_06_15, 01_07_39, 01_08_28, 01_09_30, 01_10_23, 01_11_33, 01_12_11, 01_13_28, 01_14_41, 01_15_26, 01_16_49, 01_17_34, 01_18_02, 01_19_33, 01_20_29, 02_00_51254, 02_01_82431, 02_02_17056, 02_03_34866, 02_04_6779, 02_05_65534, 02_06_65535, 02_07_65536, 02_08_65537, 02_09_65538, 02_10_100000 |
Case Name |
Status |
Exec Time |
Memory |
00_sample_01 |
AC |
1 ms |
256 KB |
01_00_01 |
AC |
1 ms |
256 KB |
01_01_19 |
AC |
1 ms |
256 KB |
01_02_31 |
AC |
1 ms |
256 KB |
01_03_22 |
AC |
1 ms |
256 KB |
01_04_31 |
AC |
1 ms |
256 KB |
01_05_40 |
AC |
1 ms |
256 KB |
01_06_15 |
AC |
1 ms |
256 KB |
01_07_39 |
AC |
1 ms |
256 KB |
01_08_28 |
AC |
1 ms |
256 KB |
01_09_30 |
AC |
1 ms |
256 KB |
01_10_23 |
AC |
1 ms |
256 KB |
01_11_33 |
AC |
1 ms |
256 KB |
01_12_11 |
AC |
1 ms |
256 KB |
01_13_28 |
AC |
1 ms |
256 KB |
01_14_41 |
AC |
1 ms |
256 KB |
01_15_26 |
AC |
1 ms |
256 KB |
01_16_49 |
AC |
1 ms |
256 KB |
01_17_34 |
AC |
1 ms |
256 KB |
01_18_02 |
AC |
1 ms |
256 KB |
01_19_33 |
AC |
1 ms |
256 KB |
01_20_29 |
AC |
1 ms |
256 KB |
02_00_51254 |
AC |
34 ms |
6232 KB |
02_01_82431 |
AC |
63 ms |
11760 KB |
02_02_17056 |
AC |
15 ms |
3056 KB |
02_03_34866 |
AC |
29 ms |
5976 KB |
02_04_6779 |
AC |
5 ms |
1152 KB |
02_05_65534 |
AC |
38 ms |
6400 KB |
02_06_65535 |
AC |
38 ms |
6392 KB |
02_07_65536 |
AC |
38 ms |
6392 KB |
02_08_65537 |
AC |
58 ms |
11512 KB |
02_09_65538 |
AC |
58 ms |
11512 KB |
02_10_100000 |
AC |
68 ms |
12128 KB |