Introduction

ここに競技プログラミング用のC++のライブラリをおいていきたい

  • Google Analyticsを使用しています

Data Structures

いろいろ, あるよね

Segment Trees

Segment Tree

Spec

  • using T = ...

    • Segment Treeに載せる型
  • segment_tree(vector<T> init)

    • initからSegment Treeを構築する
    • \( O(n) \)
  • update(i64 i, T x)

    • i番目の要素をxにする
    • \( O(\log n) \)
  • sum(i64 l, i64 r)

    • [l, r)の総和を求める
    • \( O(\log n) \)
  • find_first(i64 l, F isok)

    • isokを満たす[l, x)のsumの中でxが最小のもの
    • sumが区間の大きさについて単調である必要がある
    • ないなら-1を返す

Code

#include <vector>
using i64 = long long;

template<class T, T (*ide)(), T (*ope)(T, T)>
struct segment_tree {
  i64 n;
  std::vector<T> node;

  segment_tree() {}
  segment_tree(const std::vector<T>& init) {
    n = 1;
    while(n < init.size()) n *= 2;
    node.resize(2 * n, ide());
    for(int i = 0;i < init.size();i++) node[i + n] = init[i];
    for(int i = n; i --> 1;) node[i] = ope(node[i * 2], node[i * 2 + 1]);
  }
 
  void update(i64 i, T x) {
    i += n;
    node[i] = x;
    while(i > 1) {
      i >>= 1;
      node[i] = ope(node[i * 2], node[i * 2 + 1]);
    }
  }

  T sum(i64 l, i64 r) const {
    T lx = ide();
    T rx = ide();
    l += n;
    r += n;
    while(l < r) {
      if(l & 1) { lx = ope(lx, node[l++]); }
      if(r & 1) { rx = ope(node[--r], rx); }
      l >>= 1;
      r >>= 1;
    }
    return ope(lx, rx);
  }

  const T& at(i64 i) const {
    return node[i + n];
  }

  template<class F>
  i64 subtree_down_first(i64 i, T lx, F isok) const {
    while(i < n) {
      T next = ope(lx, node[i * 2]);
      if(isok(next)) i = i * 2;
      else {
        lx = next;
        i = i * 2 + 1;
      }
    }
    return i - n + 1;
  }

  template<class F>
  i64 find_first(i64 l, F isok) const {
    if(isok(ide())) {
      return l;
    }
    if(l == 0) {
      if(isok(node[1])) return subtree_down_first(1, ide(), isok);
      return -1;
    }
    T lx = ide();
    i64 r = n << 1;
    l += n;
    while(l < r) {
      if(l & 1) {
        T next = ope(lx, node[l]);
        if(isok(next)) return subtree_down_first(l, lx, isok);
        lx = next;
        l++;
      }
      l >>= 1;
      r >>= 1;
    }
    return -1;
  }
};

Lazy Segment Tree

Spec

  • using T = ...

    • Segment Treeに載せる型
  • using L = ...

    • 遅延伝搬させる作用素
  • T t_ide()

    • Tの単位元を返す
  • L l_ide()

    • Lの単位元を返す
  • T ope(const T& a, const T& b)

    • Tの演算を指定する
  • L lazy_ope(const L& a, const L& b)

    • Tの演算を指定する
  • T effect(const T& t, const L& l, const i64 len)

    • 長さlenの区間の総和tに作用lをする
  • lazy_segment_tree(const vector<T>& init)

    • initからLazy Segment Treeを構築する
    • \( O(n) \)
  • update(i64 a, i64 b, L lx)

    • [a, b)番目の要素に作用lxをする
    • \( O(\log n) \)
  • sum(i64 a, i64 b)

    • [a, b)の総和を求める
    • \( O(\log n) \)

Code

#include <vector>
#include <set>
#include <iostream>
using i64 = long long;

struct lazy_segment_tree {
  using T = i64;
  using L = i64;
  static inline T t_ide() { return (1LL << 31) - 1; }
  static inline L l_ide() { return (1LL << 31) - 1; }
  static inline T ope(const T& a, const T& b) { return std::min(a, b); }
  static inline L lazy_ope(const L& a, const L& b) { return b; }
  static inline T effect(const T& t, const L& l) { return l; }

  int n, h;
  std::vector<T> node;
  std::vector<L> lazy;
  std::vector<bool> flag;

  lazy_segment_tree(int N) {
    n = 1;
    h = 1;
    while(n < N) n <<= 1, h++;
    node.resize(n << 1, t_ide());
    lazy.resize(n << 1, l_ide());
    flag.resize(n << 1, false);
  }
  lazy_segment_tree(const std::vector<T>& init) {
    n = 1;
    h = 1;
    while(n < init.size()) n <<= 1, h++;
    node.resize(n << 1, t_ide());
    lazy.resize(n << 1, l_ide());
    flag.resize(n << 1, false);
    for(int i = 0;i < init.size();i++) node[i + n] = init[i];
    for(int i = n; i --> 1;) node[i] = ope(node[(i << 1)], node[(i << 1) + 1]);
  }

  inline void eff(int k, L x) {
    if(k < n << 1) {
      lazy[k] = lazy_ope(lazy[k], x);
      flag[k] = true;
    }
  }
  inline T eval(int k) const { return flag[k] ? effect(node[k], lazy[k]) : node[k]; }

  inline void push(int k) {
    if(flag[k]) {
      node[k] = eval(k);
      eff(k << 1, lazy[k]);
      eff((k << 1) | 1, lazy[k]);
      lazy[k] = l_ide();
      flag[k] = false;
    }
  }

  inline void infuse(int k) {
    k = k >> __builtin_ctz(k);
    while((k >>= 1)) node[k] = ope(eval(k << 1), eval((k << 1) + 1));
  }

  inline void infiltrate(int k) {
    if(k == n << 1) return;
    int kc = __builtin_ctz(k);
    for(int i = h; i --> kc;) push(k >> i);
  }

  inline void infiltrate(int l, int r) {
    if(r == n << 1) infiltrate(l);
    else {
      int hh = h;
      int x = l ^ r;
      for(; !(x >> --hh);) push(l >> hh);
      int lc = __builtin_ctz(l);
      for(int i = hh + 1; i --> lc;) push(l >> i);
      int rc = __builtin_ctz(r);
      for(int i = hh + 1; i --> rc;) push(r >> i);
    }
  }

  void update(int a, int b, L x) {
    int l = a + n;
    int r = b + n;
    infiltrate(l, r);
    while(l < r) {
      if(l & 1) eff(l++, x);
      if(r & 1) eff(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  T sum(int l, int r) {
    l += n;
    r += n;
    infiltrate(l, r);
    T lx = t_ide();
    T rx = t_ide();
    while(l < r) {
      if(l & 1) lx = ope(lx, eval(l++));
      if(r & 1) rx = ope(eval(--r), rx);
      l >>= 1;
      r >>= 1;
    }
    return ope(lx, rx);
  }
};

Persistent Segment Tree

Spec

  • using T = ...

    • Segment Treeに載せる型
  • T ope(const T& a, const T& b)

    • Tの演算を指定する
  • T ide()

    • Tの単位元を返す
  • persistent_segment_tree(vector<T> init)

    • initからPersistent Segment Treeを構築する
    • \( O(n) \)
  • update(i64 i, T x)

    • i番目の要素をxにしたSegment Treeを返す.
    • \( O(\log n) \)
  • sum(i64 l, i64 r)

    • [l, r)の総和を求める
    • \( O(\log n) \)

Code

#include <memory>
#include <set>
#include <vector>
using namespace std;
using i64 = long long;

struct persistent_segment_tree {
  using T = pair<i64, i64>;
  struct node {
    using Ptr = node*;
    T data;
    Ptr left;
    Ptr right;
    
    node(T data): data(data), left(), right() {}
    node(T data, Ptr left, Ptr right)
      : data(data), left(left), right(right) {}
  };

  using Ptr = node*;

  static T ope(const T& a, const T& b) { return std::min(a, b); }
  static T ide() { return {(i64)(1e18), (i64)(1e18)}; }

  Ptr root;
  i64 N;

  static Ptr build(i64 l, i64 r, const vector<T>& init) {
    if(l + 1 >= r) return new node(init[l]);
    else {
      Ptr le = build(l , (l + r) / 2, init);
      Ptr ri = build((l + r) / 2, r, init);
      T da = ope(le->data, ri->data);
      return new node(da, le, ri);
    }
  }

  static Ptr update(Ptr node, i64 i, T val, i64 l, i64 r) {
    if(i == l && i + 1 == r) return new struct node(val);
    Ptr left = nullptr;
    Ptr right = nullptr;
    if(l <= i && i < ((l + r) >> 1)) {
      left = update(node->left, i, val, l, (l + r) >> 1);
      right = node->right;
    }
    else {
      left = node->left;
      right = update(node->right, i, val, (l + r) >> 1, r);
    }
    return new struct node(ope(left->data, right->data), left, right);
  }

  static T sum(Ptr node, i64 a, i64 b, i64 l, i64 r) {
    if(b <= l || r <= a) return ide();
    else if(a <= l && r <= b) return node->data;
    else return ope(
        sum(node->left, a, b, l, (l + r) >> 1),
        sum(node->right, a, b, (l + r) >> 1, r)
        );
  }

  persistent_segment_tree(const vector<T>& init)
    : root(build(0, init.size(), init)), N(init.size()) {}
  persistent_segment_tree(Ptr root, i64 N): root(root), N(N) {}
  persistent_segment_tree update(i64 i, T x) const {
    return persistent_segment_tree(update(root, i, x, 0, N), N);
  }
  T sum(i64 l, i64 r) { return sum(root, l, r, 0, N); }
};

Dynamic Segment Tree

Spec

  • using T = ...

    • Dynamic Segment Treeに載せる型
  • T ope(const T& a, const T& b)

    • Tの演算を指定する
  • T ide()

    • Tの単位元を返す
  • dynamic_segment_tree(const i64 n)

    • [ide; n]の列を扱うDynamic Segment Treeを構築する
    • \( O(1) \)
  • update(i64 i, T x)

    • i番目の要素をxにする
    • \( O(\log n) \)
  • sum(i64 l, i64 r)

    • [l, r)の総和を求める
    • \( O(\log n) \)

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct dynamic_segment_tree {
  using T = i64;
  static T ide() { return 0ll; }
  static T ope(const T& a, const T& b) { return a + b; }

  struct node {
    node* left;
    node* right;
    T val;
    node(T v): val(v), left(nullptr), right(nullptr) {}
  };

  i64 n;
  node* root;
  dynamic_segment_tree(const i64 sz): root(new node(ide())) {
    n = 1;
    while(n < sz) n *= 2;
  }
  T value(node* n) {
    if(n) return n->val;
    else return ide();
  }
  void update(node* n, i64 i, T x, i64 l, i64 r) {
    if(l + 1 == r) {
      n->val = x;
    }
    else {
      i64 m = (l + r) / 2;
      if(i < m) {
        if(!n->left) {
          n->left = new node(ide());
        }
        update(n->left, i, x, l, m);
      }
      else {
        if(!n->right) {
          n->right = new node(ide());
        }
        update(n->right, i, x, m, r);
      }
      n->val = ope(value(n->left), value(n->right));
    }
  }

  T get(node* n, i64 a, i64 b, i64 l, i64 r) {
    if(!n) return ide();
    if(a <= l && r <= b) return value(n);
    else if(r <= a || b <= l) return ide();
    else return ope(get(n->left, a, b, l, (l + r) / 2), get(n->right, a, b, (l + r) / 2, r));
  }

  void update(i64 i, T x) {
    update(root, i, x, 0, n);
  }

  T sum(i64 a, i64 b) {
    return get(root, a, b, 0, n);
  }
};

Li-Chao Line Add Tree

Spec

  • template argments

    • class T
      • Li-Chao Segment Treeで扱う型
      • +, *, /, 比較ができる必要がある
    • const T ide
      • Tの単位元(?)
      • 例えば, 最大値を返すLiChaoなら小さい数を入れておく
    • class Compare = greater<T>
      • 最大値or最小値
  • li_chao(T mi, T, ma)

    • [mi, ma]の間の範囲を管理するLiChaoを構築する.
  • void add_line(T a, T b)

    • a * x + bの直線を追加する
    • \( O(\log L) \)
  • T get(T x)

    • max{a_i * x + b_i}を返す(Compareでminに変えられる)
    • \( O(\log L) \)

Code

#include <functional>
using namespace std;

// doubleのときは, midを変える
template<class T,const T ide,class Compare = greater<T>>
struct li_chao{
  struct Line{
    T a,b;
    Line(T a = 0,T b = 0) : a(a) , b(b) {}
    T get(T x){return a * x + b;}
  };
 
  struct Node{
    Line line;
    Node *lhs,*rhs;
    Node(Line l) : line(l) , lhs(nullptr) , rhs(nullptr){}
  };
 
  const T MI,MA;
 
  Node * root;
 
  Compare comp;
 
  T comp_get(const T & x,const T & y){
    if(comp(x , y)) return x;
    else return y;
  }
 
  li_chao(T mi , T ma) : MI(mi), MA(ma) , root(nullptr){}
 
  Node * insert(Node * p,T l,T r,Line & line){
    if(l > r) {
      return p;
    }
    if(!p) return new Node(line);
    if(comp(p->line.get(l) , line.get(l)) && comp(p->line.get(r) ,line.get(r))){
      return p;
    }
    if(!comp(p->line.get(l) , line.get(l)) && !comp(p->line.get(r) ,line.get(r))){
      p->line = line;
      return p;
    }
    T mid = (l + r) / 2;
    if(comp(line.get(mid) , p->line.get(mid))) swap(p->line , line);
    if(comp(line.get(l) , p->line.get(l))){
      p->lhs = insert(p->lhs , l , mid , line);
    }
    else{
      p->rhs = insert(p->rhs , mid + 1, r , line);
    }
    return p;
  }
 
  void add_line(T a,T b){
    Line l(a , b);
    root = insert(root,MI,MA,l);
  }
 
  T get(Node * p,T l,T r,T t){
    if(!p) return ide;
    T mid = (l + r) / 2;
    if(t <= mid) return comp_get(p->line.get(t) , get(p->lhs , l, mid,t));
    else return comp_get(p->line.get(t),get(p->rhs,mid + 1 ,r , t));
  }
 
  T get(T x){
    return get(root,MI,MA,x);
  }
};

Li-Chao Segment Add Tree

Code

#include <functional>
#include <iostream>
using namespace std;

// doubleのときは, midを変える
template<class T,const T ide,class Compare = greater<T>>
struct li_chao{
  struct Line{
    T a,b;
    bool OK;
    Line(): a(0), b(0), OK(false) {}
    Line(T a,T b) : a(a) , b(b), OK(true) {}
    T get(T x){
      if(OK) {
        return a * x + b;
      }
      else {
        return ide;
      }
    }
  };
 
  struct Node{
    Line line;
    Node *lhs,*rhs;
    Node() : line(), lhs(nullptr), rhs(nullptr) {}
    Node(Line l) : line(l) , lhs(nullptr) , rhs(nullptr){}
  };
 
  const T MI,MA;
 
  Node * root;
 
  Compare comp;
 
  T comp_get(const T & x,const T & y){
    if(comp(x , y)) return x;
    else return y;
  }
 
  li_chao(T mi , T ma) : MI(mi), MA(ma) , root(nullptr){}
 
  Node * insert(Node * p,T l,T r,Line & line){
    if(l > r) {
      return p;
    }
    if(!p) return new Node(line);
    if(comp(p->line.get(l) , line.get(l)) && comp(p->line.get(r) ,line.get(r))){
      return p;
    }
    if(!comp(p->line.get(l) , line.get(l)) && !comp(p->line.get(r) ,line.get(r))){
      p->line = line;
      return p;
    }
    T mid = (l + r) / 2;
    if(r - l == 1) {
      mid = l;
    }
    if(comp(line.get(mid) , p->line.get(mid))) swap(p->line , line);
    if(comp(line.get(l) , p->line.get(l))){
      p->lhs = insert(p->lhs , l , mid , line);
    }
    else{
      p->rhs = insert(p->rhs , mid + 1, r , line);
    }
    return p;
  }

  Node* insert_rec(Node* p, T l, T r, Line line, T a, T b) {
    if(r < a || b < l) {
      return p;
    }
    if(a <= l && r <= b) {
      return insert(p, l, r, line);
    }
    T mid = (l + r) / 2;
    if(r - l == 1) {
      mid = l;
    }
    if(!p) {
      p = new Node();
    }
    p->lhs = insert_rec(p->lhs, l, mid, line, a, b);
    p->rhs = insert_rec(p->rhs, mid + 1, r, line, a, b);
    return p;
  }
 
  void add_line(T a,T b){
    Line l(a , b);
    root = insert(root,MI,MA,l);
  }

  void add_line_range(T a, T b, T l, T r) {
    Line line(a, b);
    root = insert_rec(root, MI, MA, line, l, r);
  }
 
  T get(Node * p,T l,T r,T t){
    if(!p) return ide;
    T mid = (l + r) / 2;
    if(r - l == 1) {
      mid = l;
    }
    if(t <= mid) return comp_get(p->line.get(t) , get(p->lhs , l, mid,t));
    else return comp_get(p->line.get(t),get(p->rhs,mid + 1 ,r , t));
  }

  T get_rec(Node* p, T l, T r, T t, T a, T b) {
    if(!p) return ide;
    if(r <= a || b <= l) {
      return ide;
    }
    if(a <= l && r <= b) {
      return get(p, l, r, t);
    }
    T mid = (l + r) / 2;
    return comp_get(get_rec(p, l, mid, t, a, b), get_rec(p, mid + 1, r, t, a, b));
  }
 
  T get(T x){
    return get(root,MI,MA,x);
  }

  T get_range(T x, T l, T r) {
    return get_rec(root, MI, MA, l, r);
  }
};

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}

int main() {
  cin.tie(nullptr);
  std::ios::sync_with_stdio(false);
  i64 N, Q;
  cin >> N >> Q;
  li_chao<i64, (i64)(LLONG_MAX), std::less<i64>> seg(i64(-1e9 - 1), i64(1e9 + 1));
  while(N--) {
    i64 l, r, a, b;
    cin >> l >> r >> a >> b;
    seg.add_line_range(a, b, l, r - 1);
  }
  while(Q--) {
    i64 t;
    cin >> t;
    if(t == 0) {
      i64 l, r, a, b;
      cin >> l >> r >> a >> b;
      seg.add_line_range(a, b, l, r - 1);
    }
    else {
      i64 p;
      cin >> p;
      i64 ans = seg.get(p);
      if(ans == LLONG_MAX) {
        cout << "INFINITY" << "\n";
      }
      else {
        cout << ans << "\n";
      }
    }
  }
}

Segment Tree Beats (chmin + sum)

Code

#include <vector>
using i64 = long long;

struct segment_tree_chmin_rsq {
  using T = i64;

  const T ide = 0;
  const T m_ide = -1e18;


  std::vector<T> sum;
  std::vector<T> m1, m2;
  std::vector<i64> mcnt;
  i64 n;
  i64 h;

  void fix(int k) {
    sum[k] = sum[k * 2 + 0] + sum[k * 2 + 1];
    if(m1[k * 2 + 1] < m1[k * 2 + 0]) {
      m1[k] = m1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0];
      m2[k] = std::max(m2[k * 2 + 0], m1[k * 2 + 1]);
    }
    else if(m1[k * 2 + 0] < m1[k * 2 + 1]) {
      m1[k] = m1[k * 2 + 1];
      mcnt[k] = mcnt[k * 2 + 1];
      m2[k] = std::max(m2[k * 2 + 1], m1[k * 2 + 0]);
    }
    else {
      m1[k] = m1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0] + mcnt[k * 2 + 1];
      m2[k] = std::max(m2[k * 2 + 0], m2[k * 2 + 1]);
    }
  }

  segment_tree_chmin_rsq() {}
  segment_tree_chmin_rsq(const std::vector<i64>& vec) {
    n = 1;
    h = 1;
    while(n < vec.size()) n <<= 1, h++;
    sum.resize(2 * n);
    m1.resize(2 * n);
    m2.resize(2 * n, m_ide);
    mcnt.resize(2 * n, 0);
    for(i64 i = 0;i < vec.size();i++) {
      sum[i + n] = vec[i];
      m1[i + n] = vec[i];
      mcnt[i + n] = 1;
    }
    for(i64 i = n; i --> 1;) {
      fix(i);
    }
  }


  void eff(int k, T x) {
    sum[k] += (x - m1[k]) * mcnt[k];
    m1[k] = x;
  }

  void push(int k) {
    if(m1[k] < m1[k * 2 + 0]) eff(k * 2 + 0, m1[k]);
    if(m1[k] < m1[k * 2 + 1]) eff(k * 2 + 1, m1[k]);
  }

  void infuse(int k) {
    k = k >> __builtin_ctz(k);
    while(k >>= 1) fix(k);
  }

  void infiltrate(int k) {
    if(k == n << 1) return;
    for(int i = h; i --> 1;) push(k >> i);
  }

  void subtree_chmin(int k, T x) {
    if(m1[k] <= x) return;
    if(m2[k] < x) {
      eff(k, x);
      return;
    }
    push(k);
    subtree_chmin(k * 2 + 0, x);
    subtree_chmin(k * 2 + 1, x);
    fix(k);
  }

  void range_chmin(int a, int b, T x) {
    infiltrate(a + n);
    infiltrate(b + n);
    int l = a + n;
    int r = b + n;
    while(l < r) {
      if(l & 1) subtree_chmin(l++, x);
      if(r & 1) subtree_chmin(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  T range_sum(int l, int r) {
    l += n;
    r += n;
    infiltrate(l);
    infiltrate(r);
    T lx = ide;
    T rx = ide;
    while(l < r) {
      if(l & 1) lx = lx + sum[l++];
      if(r & 1) rx = sum[--r] + rx;
      l >>= 1;
      r >>= 1;
    }
    return lx + rx;
  }
};

#include <iostream>
using std::cout;
using std::endl;

int main() {
  segment_tree_chmin_rsq seg(std::vector<i64>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });

  cout << seg.range_sum(1, 5) << endl;
  cout << seg.range_sum(6, 10) << endl;
  seg.range_chmin(0, 16, 5);
  cout << seg.range_sum(5, 10) << endl;
}

Segment Tree Beats (chmin + chmax + sum)

Code

#include <vector>
#include <limits>
using i64 = long long;

struct segment_tree_chminmax_rsq {
  using T = i64;

  const T ide = 0;
  const T INF = std::numeric_limits<T>::max();
  const T NINF = std::numeric_limits<T>::min();


  std::vector<T> sum;
  std::vector<T> gst1, gst2;
  std::vector<T> lst1, lst2;
  std::vector<i64> mcnt;
  i64 n;
  i64 h;

  void fix(int k) {
    sum[k] = sum[k * 2 + 0] + sum[k * 2 + 1];

    if(gst1[k * 2 + 1] < gst1[k * 2 + 0]) {
      gst1[k] = gst1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0];
      gst2[k] = std::max(gst2[k * 2 + 0], gst1[k * 2 + 1]);
    }
    else if(gst1[k * 2 + 0] < gst1[k * 2 + 1]) {
      gst1[k] = gst1[k * 2 + 1];
      mcnt[k] = mcnt[k * 2 + 1];
      gst2[k] = std::max(gst2[k * 2 + 1], gst1[k * 2 + 0]);
    }
    else {
      gst1[k] = gst1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0] + mcnt[k * 2 + 1];
      gst2[k] = std::max(gst2[k * 2 + 0], gst2[k * 2 + 1]);
    }

    if(lst1[k * 2 + 0] < lst1[k * 2 + 1]) {
      lst1[k] = lst1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0];
      lst2[k] = std::min(lst2[k * 2 + 0], lst1[k * 2 + 1]);
    }
    else if(lst1[k * 2 + 1] < lst1[k * 2 + 0]) {
      lst1[k] = lst1[k * 2 + 1];
      mcnt[k] = mcnt[k * 2 + 1];
      lst2[k] = std::min(lst2[k * 2 + 1], lst1[k * 2 + 0]);
    }
    else {
      lst1[k] = lst1[k * 2 + 0];
      mcnt[k] = mcnt[k * 2 + 0] + mcnt[k * 2 + 1];
      lst2[k] = std::min(lst2[k * 2 + 0], lst2[k * 2 + 1]);
    }
  }

  segment_tree_chmin_rsq() {}
  segment_tree_chmin_rsq(const std::vector<i64>& vec) {
    n = 1;
    h = 1;
    while(n < vec.size()) n <<= 1, h++;
    sum.resize(2 * n);
    gst1.resize(2 * n);
    gst2.resize(2 * n, NINF);
    lst1.resize(2 * n);
    lst2.resize(2 * n, INF);
    mcnt.resize(2 * n, 0);
    for(i64 i = 0;i < vec.size();i++) {
      sum[i + n] = vec[i];
      gst1[i + n] = vec[i];
      lst1[i + n] = vec[i];
      mcnt[i + n] = 1;
    }
    for(i64 i = n; i --> 1;) {
      fix(i);
    }
  }

  void eff_chmin(int k, T x) {
    sum[k] += (x - gst1[k]) * mcnt[k];
    if(gst1[k] == lst1[k]) {
      gst1[k] = lst1[k] = x;
    }
    else if(gst1[k] == lst2[k]) {
      gst1[k] = lst2[k] = x;
    }
    else {
      gst1[k] = x;
    }
  }

  void eff_chmax(int k, T x) {
    sum[k] += (x - lst1[k]) * mcnt[k];
    if(lst1[k] == gst1[k]) {
      lst1[k] = gst1[k] = x;
    }
    else if(lst1[k] == gst2[k]) {
      lst1[k] = gst2[k] = x;
    }
    else {
      lst1[k] = x;
    }
  }

  void push(int k) {
    if(gst1[k] < gst1[k * 2 + 0]) eff_chmin(k * 2 + 0, gst1[k]);
    if(gst1[k] < gst1[k * 2 + 1]) eff_chmin(k * 2 + 1, gst1[k]);

    if(lst1[k] > lst1[k * 2 + 0]) eff_chmax(k * 2 + 0, gst1[k]);
    if(lst1[k] > lst1[k * 2 + 1]) eff_chmax(k * 2 + 1, gst1[k]);
  }

  void infuse(int k) {
    k = k >> __builtin_ctz(k);
    while(k >>= 1) fix(k);
  }

  void infiltrate(int k) {
    if(k == n << 1) return;
    for(int i = h; i --> 1;) push(k >> i);
  }

  void subtree_chmin(int k, T x) {
    if(gst1[k] <= x) return;
    if(gst2[k] < x) {
      eff_chmin(k, x);
      return;
    }
    push(k);
    subtree_chmin(k * 2 + 0, x);
    subtree_chmin(k * 2 + 1, x);
    fix(k);
  }

  void subtree_chmax(int k, T x) {
    if(x <= lst1[k]) return;
    if(x < lst2[k]) {
      eff_chmax(k, x);
      return;
    }
    push(k);
    subtree_chmax(k * 2 + 0, x);
    subtree_chmax(k * 2 + 1, x);
    fix(k);
  }

  void range_chmin(int a, int b, T x) {
    infiltrate(a + n);
    infiltrate(b + n);
    int l = a + n;
    int r = b + n;
    while(l < r) {
      if(l & 1) subtree_chmin(l++, x);
      if(r & 1) subtree_chmin(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  void range_chmax(int a, int b, T x) {
    infiltrate(a + n);
    infiltrate(b + n);
    int l = a + n;
    int r = b + n;
    while(l < r) {
      if(l & 1) subtree_chmax(l++, x);
      if(r & 1) subtree_chmax(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  T range_sum(int l, int r) {
    l += n;
    r += n;
    infiltrate(l);
    infiltrate(r);
    T lx = ide;
    T rx = ide;
    while(l < r) {
      if(l & 1) lx = lx + sum[l++];
      if(r & 1) rx = sum[--r] + rx;
      l >>= 1;
      r >>= 1;
    }
    return lx + rx;
  }
};

#include <iostream>
using std::cout;
using std::endl;

int main() {
  segment_tree_chmin_rsq seg(std::vector<i64>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });

  cout << seg.range_sum(1, 5) << endl;
  cout << seg.range_sum(6, 10) << endl;
  seg.range_chmax(0, 5, 2);
  seg.range_chmin(0, 5, 3);
  cout << seg.range_sum(0, 5) << endl;
}

Segment Tree Beats (chmin + chmax + add + sum)

Code

#include <vector>
#include <limits>
using i64 = long long;

struct segment_tree_chminmaxadd_rsq {
  using T = i64;

  const T ide = 0;
  static const T INF = std::numeric_limits<T>::max();
  static const T NINF = std::numeric_limits<T>::min();

  struct node {
    T sum;
    T gst1, gst2, gcnt;
    T lst1, lst2, lcnt;
    i64 len;
    i64 add;
    node(): gst2(NINF), gcnt(1), lst2(INF), lcnt(1), len(1), add(0) {}
  };

  std::vector<node> v;

  i64 n;
  i64 h;

  void fix(int k) {
    node& p = v[k];
    node& l = v[k * 2 + 0];
    node& r = v[k * 2 + 1];
    p.sum = l.sum + r.sum;

    if(r.gst1 < l.gst1) {
      p.gst1 = l.gst1;
      p.gcnt = l.gcnt;
      p.gst2 = std::max(l.gst2, r.gst1);
    }
    else if(l.gst1 < r.gst1) {
      p.gst1 = r.gst1;
      p.gcnt = r.gcnt;
      p.gst2 = std::max(l.gst1, r.gst2);
    }
    else {
      p.gst1 = l.gst1;
      p.gcnt = l.gcnt + r.gcnt;
      p.gst2 = std::max(l.gst2, r.gst2);
    }

    if(r.lst1 > l.lst1) {
      p.lst1 = l.lst1;
      p.lcnt = l.lcnt;
      p.lst2 = std::min(l.lst2, r.lst1);
    }
    else if(l.lst1 > r.lst1) {
      p.lst1 = r.lst1;
      p.lcnt = r.lcnt;
      p.lst2 = std::min(l.lst1, r.lst2);
    }
    else {
      p.lst1 = l.lst1;
      p.lcnt = l.lcnt + r.lcnt;
      p.lst2 = std::min(l.lst2, r.lst2);
    }

  }

  segment_tree_chminmaxadd_rsq() {}
  segment_tree_chminmaxadd_rsq(const std::vector<i64>& vec) {
    n = 1;
    h = 1;
    while(n < vec.size()) n <<= 1, h++;
    v.resize(2 * n);
    for(i64 i = 0;i < vec.size();i++) {
      v[i + n].sum = vec[i];
      v[i + n].gst1 = vec[i];
      v[i + n].lst1 = vec[i];
    }
    for(i64 i = n; i --> 1;) {
      fix(i);
      v[i].len = v[i * 2 + 0].len + v[i * 2 + 1].len;
    }
  }

  void eff_add(int k, T x) {
    auto& p = v[k];
    p.sum  += x * p.len;
    p.gst1 += x;
    p.lst1 += x;
    p.add += x;
    if(p.gst2 != NINF) p.gst2 += x;
    if(p.lst2 !=  INF) p.lst2 += x;
  }

  void eff_chmin(int k, T x) {
    auto& p = v[k];
    p.sum += (x - p.gst1) * p.gcnt;
    if(p.gst1 == p.lst1) {
      p.gst1 = p.lst1 = x;
    }
    else if(p.gst1 == p.lst2) {
      p.gst1 = p.lst2 = x;
    }
    else {
      p.gst1 = x;
    }
  }

  void eff_chmax(int k, T x) {
    auto& p = v[k];
    p.sum += (x - p.lst1) * p.lcnt;
    if(p.lst1 == p.gst1) {
      p.lst1 = p.gst1 = x;
    }
    else if(p.lst1 == p.gst2) {
      p.lst1 = p.gst2 = x;
    }
    else {
      p.lst1 = x;
    }
  }

  void push(int k) {
    if(k >= n) return;
    auto& p = v[k];
    if(p.add != 0) {
      eff_add(k * 2 + 0, p.add);
      eff_add(k * 2 + 1, p.add);
      p.add = 0;
    }
    if(p.gst1 < v[k * 2 + 0].gst1) eff_chmin(k * 2 + 0, p.gst1);
    if(p.gst1 < v[k * 2 + 1].gst1) eff_chmin(k * 2 + 1, p.gst1);

    if(p.lst1 > v[k * 2 + 0].lst1) eff_chmax(k * 2 + 0, p.lst1);
    if(p.lst1 > v[k * 2 + 1].lst1) eff_chmax(k * 2 + 1, p.lst1);
  }

  void infuse(int k) {
    k = k >> __builtin_ctz(k);
    while(k >>= 1) fix(k);
  }

  inline void infiltrate(int k) {
    if(k == n << 1) return;
    int kc = __builtin_ctz(k);
    for(int i = h; i --> kc;) push(k >> i);
  }

  inline void infiltrate(int l, int r) {
    if(r == n << 1) infiltrate(l);
    else {
      int hh = h;
      int x = l ^ r;
      for(; !(x >> --hh) && hh;) push(l >> hh);
      int lc = __builtin_ctz(l);
      for(int i = hh + 1; i --> lc;) push(l >> i);
      int rc = __builtin_ctz(r);
      for(int i = hh + 1; i --> rc;) push(r >> i);
    }
  }

  void subtree_chmin(int k, T x) {
    if(v[k].gst1 <= x) return;
    if(v[k].gst2 < x) {
      eff_chmin(k, x);
      return;
    }
    push(k);
    subtree_chmin(k * 2 + 0, x);
    subtree_chmin(k * 2 + 1, x);
    fix(k);
  }

  void subtree_chmax(int k, T x) {
    if(x <= v[k].lst1) return;
    if(x < v[k].lst2) {
      eff_chmax(k, x);
      return;
    }
    push(k);
    subtree_chmax(k * 2 + 0, x);
    subtree_chmax(k * 2 + 1, x);
    fix(k);
  }

  void range_chmin(int a, int b, T x) {
    int l = a + n;
    int r = b + n;
    infiltrate(l, r);
    while(l < r) {
      if(l & 1) subtree_chmin(l++, x);
      if(r & 1) subtree_chmin(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  void range_chmax(int a, int b, T x) {
    int l = a + n;
    int r = b + n;
    infiltrate(l, r);
    while(l < r) {
      if(l & 1) subtree_chmax(l++, x);
      if(r & 1) subtree_chmax(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  void range_add(int a, int b, T x) {
    int l = a + n;
    int r = b + n;
    infiltrate(l, r);
    while(l < r) {
      if(l & 1) eff_add(l++, x);
      if(r & 1) eff_add(--r, x);
      l >>= 1;
      r >>= 1;
    }
    infuse(a + n);
    infuse(b + n);
  }

  T range_sum(int l, int r) {
    l += n;
    r += n;
    infiltrate(l, r);
    T lx = ide;
    T rx = ide;
    while(l < r) {
      if(l & 1) lx = lx + v[l++].sum;
      if(r & 1) rx = v[--r].sum + rx;
      l >>= 1;
      r >>= 1;
    }
    return lx + rx;
  }
};

Heap

Pairing Heap

マージ可能で高速なHeap

Spec

  • top()

    • 最小値を返す
    • \( O(1) \)
  • pop()

    • 最小値の要素を削除する
    • \( O(\log n) \)
  • push(const T& x)

    • 要素xを追加する
    • \( O(1) \)
  • meld(h)

    • ヒープhとマージする
    • \( O(1) \)

Code

#include <vector>
using namespace std;

template<class T, class Compare>
struct pairing_heap {
  struct node {
    T val;
    node* head;
    node* next;
    node(const T& v) : val(v), head(nullptr), next(nullptr) {  }
  };
  size_t sz;
  node* root;
  Compare comp;
  pairing_heap() : sz(0), root(nullptr) {  }
  node* merge(node* x, node* y) {
    if(!y) return x;
    if(!x) return y;
    if(!comp(x->val, y->val)) swap(x,y);
    y->next = x->head;
    x->head = y;
    return x;
  }
  node* mergeList(node * x) {
    node* nn = nullptr;
    while(x) {
      node* a = x;
      node* b = nullptr;
      x = x->next;
      a->next = nullptr;
      if(x) {
        b = x;
        x = x->next;
        b->next = nullptr;
      }
      a = merge(a, b);
      a->next = nn;
      nn = a;
    }
    while(nn) {
      node* j = nn;
      nn = nn->next;
      x = merge(j,x);
    }
    return x;
  }
  
  /* return compest element */
  T top() {
    return root->val;
  }

  /* pop compest element */
  void pop() {
    --sz;
    node* te = root;
    root = mergeList(root->head);
    delete te, te = nullptr;
  }
  
  /* add element */
  void push(const T& x) {
    ++sz;
    root = merge(new node(x), root);
  }
  
  /* size */
  size_t size() {
    return sz;
  }
  
  /* merge heap */
  void meld(pairing_heap<T,Compare>& h) {
    root = merge(root, h.root);
    h.root = nullptr;
    h.sz = 0;
  }
};

Trees

木を処理するやつ

EulerTour Subtree

EulerTourの部分木を処理するバージョン
木を列に落とし込んだときの部分木の範囲がわかるので, Segment Treeと合わせて使うといい.

LCAをET Subtree + RMQで求める場合はコメントアウトしてある部分を使う.

Spec

  • eulertour_subtree(i64 n)

    • n頂点の木を構築する準備
  • add_edge(i64 u, i64 v)

    • 頂点uvを結ぶ辺を追加する
  • start_tour(i64 r)

    • rを根としてEulerTourを行う
    • \( O(n) \)
  • subtree_range(i64 v)

    • 頂点vの部分木に対応する範囲を返す
    • \( O(1) \)
  • vertex(i64 v)

    • 頂点vに対応するindexを返す.
    • \( O(1) \)

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct eulertour_subtree {
  vector<vector<i64>> G;
  vector<i64> tour;
  vector<i64> L, R;
  vector<i64> depth;
  eulertour_subtree(i64 n): G(n), L(n), R(n), depth(n) {}
  void add_edge(i64 u, i64 v) {
    G[u].push_back(v);
    G[v].push_back(u);
  }

  void dfs(i64 v, i64 f, i64 d) {
    tour.push_back(v);
    L[v] = tour.size() - 1;
    depth[v] = d;
    for(auto to: G[v]) {
      if(to == f) continue;
      dfs(to, v, d + 1);
      //tour.push_back(v);
    }
    R[v] = tour.size() - 1;
  }

  void start_tour(i64 r) {
    dfs(r, -1, 0);
  }

  //[L[v], R[v])
  pair<i64, i64> subtree_range(i64 v) {
    return pair<i64, i64>(L[v], R[v]);
  }

  i64 vertex(i64 v) {
    return L[v];
  }
};

EulerTour Path

EulerTourのパスを処理するバージョン
パスとして求められるのは, 上から下に降りるようなパスだけなので, 任意のパスを扱うときはLCAをしないといけない.
扱える要素には, 可逆性, 可換性(?)が必要.

Spec

  • euler_tour_path(i64 n)

    • n頂点の木を構築する準備
  • add_edge(i64 u, i64 v)

    • 頂点uvを結ぶ辺を追加する
  • start_tour(i64 r)

    • rを根としてEulerTourを行う
    • \( O(n) \)
  • edge_in(i64 v)

    • 頂点vに入る辺のindexを返す
    • \( O(1) \)
  • edge_out(i64 v)

    • 頂点vに出る辺のindexを返す
    • \( O(1) \)
  • path_range(i64 u, i64 v)

    • 頂点uから降りて頂点vに辿るパスの範囲を返す.
    • \( O(1) \)

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct eulartour_path {
  vector<vector<i64>> G;
  vector<i64> in, out;
  i64 cnt;
  eulartour_path(i64 n): G(n), in(n), out(n) {}
  void add_edge(i64 u, i64 v) {
    G[u].push_back(v);
    G[v].push_back(u);
  }

  void dfs(i64 v, i64 f) {
    for(auto to: G[v]) {
      if(to == f) continue;
      in[to] = cnt;
      cnt++;
      dfs(to, v);
      out[to] = cnt;
      cnt++;
    }
  }

  void start_tour(i64 r) {
    in[r] = cnt;
    cnt++;
    dfs(r, -1);
  }

  i64 edge_in(i64 v) { return in[v]; }
  i64 edge_out(i64 v) { return out[v]; }
  pair<i64, i64> path_range(i64 u, i64 v) {
    return { in[u] + 1, in[v] + 1 };
  }
};


Heavy Light Decomposition

静的木

木のパスや部分木のクエリを処理できる.

Spec

  • HeavyLightDecomposition(i64 n)

    • 頂点数nで初期化
  • add_edge(i64 u, i64 v)

    • 頂点uvをつなぐ
  • build(i64 r)

    • rを根としてHLDecompを行う
  • sequence()

    • HLDecompしたときのオイラーツアーの配列.
  • path(i64 a, i64 b, bool edge)

    • パスの列を返す.
    • edge = trueで辺に対するパスを返す.
    • \( O(\log n) \)
  • subtree(i64 v, bool edge)

    • 部分木の列を返す.
    • edge = trueで辺に対するパスを返す.
    • \( O(1) \)

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct HeavyLightDecomposition {
  vector<vector<i64>> G;
  vector<i64> in, out;
  vector<i64> sz;
  vector<i64> seq;
  vector<i64> next;
  vector<i64> par;


  HeavyLightDecomposition(i64 n)
    : G(n), in(n), out(n), sz(n), next(n, -1), par(n, -1) {}

  void add_edge(i64 u, i64 v) {
    G[u].push_back(v);
    G[v].push_back(u);
  }

  void dfs_sz(i64 v, i64 f) {
    sz[v] = 1;
    for(i64 i = 0;i < G[v].size();i++) {
      i64 x = G[v][i];
      if(x == f) continue;
      dfs_sz(x, v);
      par[x] = v;
      sz[v] += sz[x];
      if(sz[G[v][0]] < sz[G[v][i]]) { swap(G[v][0], G[v][i]); }
    }
  }

  i64 dfs_eul(i64 v, i64 f, i64 t) {
    in[v] = t++;
    seq.push_back(v);
    for(i64 i = 0;i < G[v].size();i++) {
      i64 x = G[v][i];
      if(x == f) continue;
      next[x] = (i == 0) ? next[v] : x;
      t = dfs_eul(x, v, t);
    }
    return out[v] = t;
  }

  void build(i64 r) {
    dfs_sz(r, -1);
    dfs_eul(r, -1, 0);
  }

  const vector<i64>& sequence() const { return seq; }
  i64 lca(i64 a, i64 b) const {
    while(true) {
      if(in[b] > in[a]) swap(a, b);
      if(next[b] == next[a]) return b;
      a = par[next[a]];
    }
  }

  pair<vector<pair<i64, i64>>, vector<pair<i64, i64>>> path(i64 a, i64 b, bool edge) const {
    vector<pair<i64, i64>> l, r;
    while(true) {
      if(in[b] > in[a]) { swap(a, b); swap(l, r); }
      if(next[b] == next[a]) {
        l.push_back({ in[b] + !!edge, in[a] + 1 });
        break;
      }
      l.push_back({ in[next[a]], in[a] + 1 });
      a = par[next[a]];
    }
    return { std::move(l), std::move(r) };
  }

  pair<i64, i64> subtree(i64 v, bool edge) { return { in[v] + !!edge, out[v] }; }
};

Link Cut Tree

動的木

パスのsumを計算したり, パスに対する作用を遅延伝搬できる.

Spec

  • struct node

    • link cut treeで扱うノードの構造体
    • この中に載せたいデータを載せる
  • fix(node * n)

    • ノードの情報の再計算をする
  • reverse(node * n)

    • 平衡二分木の反転
    • モノイドの演算順序が反転するのでその処理を書く
    • モノイドが可換であれば問題ない
  • lazy(node * n, i64 l)

    • 遅延伝搬するときの演算
    • expose(n); lazy(n, x)をすると, [root, n]のパスにxを作用させることになる
  • push(node* n)

    • 遅延伝搬
    • lazyを変えている場合はここも変更
  • expose(node* n)

    • nをLink Cut Treeの根として, その木が[root, n]のパスをあらわすようになる
  • link(node* p, node* c)

    • pを親, cを子として繋げる
  • cut(node* c)

    • cの親とつながっている辺を切る
  • evert(node* t)

  • tを親にする

Code

パス加算, パスsumを処理している

include <cstdint>
#include <utility>
#include <string>
#include <iostream>

using i64 = long long;

namespace lctree {

  struct R {
    int a;
    R(): a(0) {}
    R(int a): a(a) {}
  };
  struct V {
    int a;
    V(): a(0) {}
    V(int a): a(a) {}
  };
  inline V compress(const V& a, const V& b) { return V(a.a + b.a); }
  inline V rake_merge(const V& a, const R& b) { return V(a.a + b.a); }
  inline V reverse(const V& a) { return a; }
  inline void rake_plus(R& a, const V& b) { a.a += b.a; }
  inline void rake_minus(R& a, const V& b) { a.a -= b.a; }

  struct node;
  extern struct node n[505050];
  extern int ni;

  using node_index = std::uint_least32_t;
  using size_type = std::size_t;

  struct node {
    node_index c[3];
    V v; V f; R r;
    bool rev;
    node(): rev(false) { c[0] = c[1] = c[2] = 0; }
    node& operator[](int d) { return n[c[d]]; }
  };

  inline node_index new_node(V v) { n[ni].v = v; n[ni].f = v; return ni++; }
  inline void reverse(node_index i) {
    n[i].v = reverse(n[i].v);
    n[i].f = reverse(n[i].f);
    n[i].rev ^= true;
  }
  inline void push(node_index i) {
    if(n[i].rev) {
      std::swap(n[i].c[0], n[i].c[1]);
      if(n[i].c[0]) reverse(n[i].c[0]);
      if(n[i].c[1]) reverse(n[i].c[1]);
      n[i].rev = false;
    }
  }
  inline void fix(node_index i) {
    push(i);
    n[i].f = compress(compress(n[i][0].f, n[i].v), rake_merge(n[i][1].f, n[i].r));
  }

  inline int child_dir(node_index i) {
    if(n[i].c[2]) {
      if(n[i][2].c[0] == i) { return 0; }
      else if(n[i][2].c[1] == i) { return 1; }
    }
    return 3;
  }

  inline void rotate(node_index x, size_type dir) {
    node_index p = n[x].c[2];
    int x_dir = child_dir(x);
    node_index y = n[x].c[dir ^ 1];

    n[n[y][dir].c[2] = x].c[dir ^ 1] = n[y].c[dir];
    n[n[x].c[2] = y].c[dir] = x;
    n[y].c[2] = p;
    if(x_dir < 2) n[p].c[x_dir] = y;
    fix(n[x].c[dir ^ 1]);
    fix(x);
  }

  void splay(node_index i) {
    push(i);
    int i_dir;
    int j_dir;
    while(child_dir(i) < 2) {
      node_index j = n[i].c[2];
      if(child_dir(j) < 2) {
        node_index k = n[j].c[2];
        push(k), push(j), push(i);
        i_dir = child_dir(i);
        j_dir = child_dir(j);
        if(i_dir == j_dir) rotate(k, j_dir ^ 1), rotate(j, i_dir ^ 1);
        else rotate(j, i_dir ^ 1), rotate(k, j_dir ^ 1);
      }
      else push(j), push(i), rotate(j, child_dir(i) ^ 1);
    }
    fix(i);
  }

  node_index expose(node_index i) {
    node_index right = 0;
    node_index ii = i;
    while(i) {
      splay(i);
      rake_minus(n[i].r, n[right].f);
      rake_plus(n[i].r, n[i][1].f);
      n[i].c[1] = right;
      fix(i);
      right = i;
      i = n[i].c[2];
    }
    splay(ii);
    return ii;
  }

  void link(node_index i, node_index j) {
    if(!i || !j) return;
    expose(i);
    expose(j);
    n[n[j].c[2] = i].c[1] = j;
    fix(i);
  }

  void cut(node_index i) {
    if(!i) return;
    expose(i);
    node_index p = n[i].c[0];
    n[i].c[0] = n[p].c[2] = 0;
    fix(i);
  }

  void evert(node_index i) {
    if(!i) return;
    expose(i);
    reverse(i);
    push(i);
  }

  node n[505050];
  int ni = 1;

  int all_tree(node_index i) {
    expose(i);
    return n[i].f.a;
  }
}

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}

template<class T, class Cond>
struct chain {
  Cond cond; chain(Cond cond) : cond(cond) {}
  bool operator()(T& a, const T& b) const {
    if(cond(a, b)) { a = b; return true; }
    return false;
  }
};
template<class T, class Cond>
chain<T, Cond> make_chain(Cond cond) { return chain<T, Cond>(cond); }

#include <cstdio>
 
namespace niu {
  char cur;
  struct FIN {
    static inline bool is_blank(char c) { return c <= ' '; }
    inline char next() { return cur = getc_unlocked(stdin); }
    inline char peek() { return cur; }
    inline void skip() { while(is_blank(next())){} }
#define intin(inttype)  \
    FIN& operator>>(inttype& n) { \
      bool sign = 0; \
      n = 0; \
      skip(); \
      while(!is_blank(peek())) { \
        if(peek() == '-') sign = 1; \
        else n = (n << 1) + (n << 3) + (peek() & 0b1111); \
        next(); \
      } \
      if(sign) n = -n; \
      return *this; \
    }
intin(int)
intin(long long)
  } fin;
 
  char tmp[128];
  struct FOUT {
    static inline bool is_blank(char c) { return c <= ' '; }
    inline void push(char c) { putc_unlocked(c, stdout); }
    FOUT& operator<<(char c) { push(c); return *this; }
    FOUT& operator<<(const char* s) { while(*s) push(*s++); return *this; }
#define intout(inttype) \
    FOUT& operator<<(inttype n) { \
      if(n) { \
        char* p = tmp + 127; bool neg = 0; \
        if(n < 0) neg = 1, n = -n; \
        while(n) *--p = (n % 10) | 0b00110000, n /= 10; \
        if(neg) *--p = '-'; \
        return (*this) << p; \
      } \
      else { \
        push('0'); \
        return *this; \
      } \
    }
intout(int)
intout(long long)
  } fout;
}

int main() {
  using niu::fin;
  using niu::fout;
  i64 N, Q;
  fin >> N;
  vector<vector<int>> vs(N);
  vector<int> co(N);
  for(int i = 0;i < N;i++) {
    lctree::new_node(1);
    int a;
    fin >> a;
    a--;
    co[i] = a;
    vs[a].push_back(i);
  }
  vector<vector<int>> G(N);
  for(int i = 0;i + 1 < N;i++) {
    i64 a, b;
    fin >> a >> b;
    a--;
    b--;
    G[a].push_back(b);
    if(co[a] != co[b])
      G[b].push_back(a);
    lctree::evert(b + 1);
    lctree::link(a + 1, b + 1);
  }
 
  auto func = [&](i64 ans, i64 a, i64 b) {
    i64 A = lctree::all_tree(a);
    ans -= A * (A + 1) / 2;
    lctree::evert(a);
    lctree::cut(b);
    i64 B = lctree::all_tree(a);
    ans += B * (B + 1) / 2;
    i64 C = lctree::all_tree(b);
    ans += C * (C + 1) / 2;
    //std::cout << A << " " << B << " " << C << std::endl;
    return ans;
  };
  for(int i = 0;i < N;i++) {
    i64 ans = (N - vs[i].size()) * ((N - vs[i].size()) + 1) / 2;
    for(auto v: vs[i]) {
      lctree::expose(v + 1);
      lctree::n[v + 1].v.a = 0;
      lctree::fix(v + 1);
    }
    for(auto v: vs[i]) {
      for(auto t: G[v]) {
        ans = func(ans, v + 1, t + 1);
      }
    }
    //cout << ans << endl;
    fout << (N * (N + 1) / 2) - ans << "\n";
    for(auto v: vs[i]) {
      lctree::expose(v + 1);
      lctree::n[v + 1].v.a = 1;
      lctree::fix(v + 1);
    }
    for(auto v: vs[i]) {
      for(auto t: G[v]) {
        lctree::evert(t + 1);
        lctree::link(v + 1, t + 1);
      }
    }
  }
}
 

Top Tree

なにこれ

Spec

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}

template<class T, class Cond>
struct chain {
  Cond cond; chain(Cond cond) : cond(cond) {}
  bool operator()(T& a, const T& b) const {
    if(cond(a, b)) { a = b; return true; }
    return false;
  }
};
template<class T, class Cond>
chain<T, Cond> make_chain(Cond cond) { return chain<T, Cond>(cond); }

#include <vector>
#include <iostream>
#include <string>
#include <cassert>
using i64 = long long;

namespace toptree {
  struct cluster {
    int length;

    using V = int;
    cluster(i64 l = 0): length(l) {}
    static cluster identity() {
      return cluster(0);
    }
    static V v_identity() {
      return 0;
    }
    static cluster compress(const cluster& a, const cluster& b, V, V, V cv) {
      return cluster(
          a.length + b.length + cv
          ); }
    static cluster rake(const cluster& a, const cluster& b, V av, V bv, V cv) {
      return cluster(
          a.length + b.length + bv
          );
    }
    static cluster reverse(const cluster& c) {
      return c;
    }
    static std::size_t select(const cluster&, const cluster&, V, V, V) {
      return 0;
    }
  };

  struct vertex;
  struct node;

  using size_type = std::size_t;
  using node_index = std::uint_least32_t;
  using vertex_index = std::uint_least32_t;

  extern struct vertex v[404040];
  extern size_type vi;
  extern struct node n[2020202];
  extern size_type ni;
  extern node_index guard;

  void link(node_index a, node_index b, cluster weight);

  struct vertex {
    cluster::V val;
    node_index hn;
  };

   vertex_index new_vertex(cluster::V val) {
    v[vi++] = { val, 0 };
    v[vi++] = { cluster::v_identity(), 0 };
    link(vi - 2, vi - 1, cluster::identity());
    return vi - 2;
  }


  enum class type { Compress, Rake, Edge };


  struct node {
    node_index i;
    node_index c[4];
    bool rev;
    cluster f;
    vertex_index v[2];
    type ty;

    inline node& operator[](size_type d) { return n[c[d]]; }
    inline vertex& operator()(size_type d) { return toptree::v[this->v[d]]; }
  };

  inline node_index new_node(type ty) {
    node_index i = ni++;
    n[i].i = i;
    n[i].ty = ty;
    return i;
  }

   void reverse(node_index i) {
    std::swap(n[i].v[0], n[i].v[1]);
    n[i].f = cluster::reverse(n[i].f);
    n[i].rev ^= true;
  }

   void push(node_index i) {
    if(n[i].ty != type::Edge && n[i].rev) {
      std::swap(n[i].c[0], n[i].c[1]);
      reverse(n[i].c[0]);
      reverse(n[i].c[1]);
      n[i].rev = false;
    }
  }

    void fix(node_index i) {
    push(i);
    if(n[i].ty == type::Compress) {
      n[i].v[0] = n[i][0].v[0];
      n[i].v[1] = n[i][1].v[1];
      cluster l = n[i][0].f;
      if(n[i].c[2])
        l = cluster::rake(l, n[i][2].f, n[i][0](0).val, n[i][2](0).val, n[i][0](1).val);
      n[i].f = cluster::compress(l, n[i][1].f, n[i][0](0).val, n[i][1](1).val, n[i][0](1).val);
    }
    if(n[i].ty == type::Rake) {
      n[i].v[0] = n[i][0].v[0];
      n[i].v[1] = n[i][0].v[1];
      n[i].f = cluster::rake(n[i][0].f, n[i][1].f, n[i][0](0).val, n[i][1](0).val, n[i][0](1).val);
    }

    if(n[i].ty == type::Compress)
      n[i][1](0).hn = i;
    if(n[i].ty != type::Rake) {
      if(!n[i].c[3])
        n[i](0).hn = n[i](1).hn = i;
      else if(n[i][3].ty == type::Rake || n[i][3].c[2] == n[i].i)
        n[i](0).hn = i;
    }
  }


   int child_dir(node_index i) {
    if(n[i].c[3]) {
      if(n[i][3].c[0] == i) { return 0; }
      else if(n[i][3].c[1] == i) { return 1; }
      else { return 2; }
    }
    return 3;
  }

   void rotate(node_index x, size_type dir) {
    node_index p = n[x].c[3];
    int x_dir = child_dir(x);
    node_index y = n[x].c[dir ^ 1];

    n[n[y][dir].c[3] = x].c[dir ^ 1] = n[y].c[dir];
    n[n[x].c[3] = y].c[dir] = x;
    n[y].c[3] = p;
    if(x_dir < 2) n[p].c[x_dir] = y;
    fix(n[x].c[dir ^ 1]);
    fix(x);
  }

   void splay(node_index i) {
    push(i);
    int i_dir;
    int j_dir;
    while(child_dir(i) < 2 && n[i].c[3] != guard && n[i].ty == n[i][3].ty) {
      node_index j = n[i].c[3];
      if(child_dir(j) < 2 && n[j].c[3] != guard && n[j].ty == n[j][3].ty) {
        node_index k = n[j].c[3];
        push(k), push(j), push(i);
        i_dir = child_dir(i);
        j_dir = child_dir(j);
        if(i_dir == j_dir) rotate(k, j_dir ^ 1), rotate(j, i_dir ^ 1);
        else rotate(j, i_dir ^ 1), rotate(k, j_dir ^ 1);
      }
      else push(j), push(i), rotate(j, child_dir(i) ^ 1);
    }
    fix(i);
  }

   node_index expose_raw(node_index i) {
    while(true) {
      if(n[i].ty == type::Compress) splay(i);
      node_index p = n[i].c[3];
      if(!p) break;
      else if(n[p].ty == type::Rake) {
        splay(p);
        p = n[p].c[3];
      }
      else if(p == toptree::guard && child_dir(i) < 2) break;

      splay(p);

      int dir = child_dir(p);
      dir = (dir >= 2 || n[p][3].ty == type::Rake) ? 0 : dir;
      if(dir == 1) {
        reverse(n[p].c[dir]);
        push(n[p].c[dir]);
        reverse(i);
        push(i);
      }

      int i_dir = child_dir(i);
      int x = n[i].c[3];
      int m = n[p].c[dir];

      n[n[m].c[3] = x].c[i_dir] = m;
      n[n[i].c[3] = p].c[dir] = i;
      fix(m); fix(x); fix(i); fix(p);
      if(n[i].ty == type::Edge) {
        i = p;
      }
    }
    return i;
  }

   node_index expose(vertex_index i) {
    return expose_raw(v[i].hn);
  }

   void soft_expose(vertex_index a, vertex_index b) {
    node_index r = expose(a);
    if(v[a].hn == v[b].hn) {
      if(n[r].c[1] == a || n[r].c[0] == b) reverse(r), push(r);
      return;
    }
    guard = r;
    node_index s = expose(b);
    guard = ~0;
    fix(r);
    if(child_dir(s) == 0) reverse(r), push(r);
  }

   void link(vertex_index a, vertex_index b, cluster weight) {
    node_index e = new_node(type::Edge);
    n[e].v[0] = a; n[e].v[1] = b; n[e].f = weight;
    if(!v[a].hn && !v[b].hn) { fix(e); return; }
    node_index na = v[a].hn;
    node_index nb = v[b].hn;
    node_index left;
    for(int dir = 0; dir < 2; dir++) {
      if(!nb) left = e;
      else {
        nb = expose_raw(nb);
        if(n[nb].v[dir ^ 1] == b) {
          reverse(nb);
          push(nb);
        }
        if(n[nb].v[dir] == b) {
          left = new_node(type::Compress);
          n[left].c[dir] = e; n[left].c[dir ^ 1] = nb;
          n[e].c[3] = n[nb].c[3] = left;
          fix(e); fix(nb); fix(left);
        }
        else {
          node_index ch = n[nb].c[dir];
          if(dir) reverse(ch);
          n[n[e].c[3] = nb].c[dir] = e;
          node_index beta = n[nb].c[2];
          node_index rake;
          if(beta) {
            rake = new_node(type::Rake);
            n[rake].c[0] = beta; n[rake].c[1] = ch;
            n[beta].c[3] = n[ch].c[3] = rake;
            fix(beta); fix(ch);
          }
          else rake = ch;
          n[n[rake].c[3] = nb].c[2] = rake;
          fix(rake); fix(e); fix(left = nb);
        }
      }
      e = left;
      nb = na;
      b = a;
    }
  }

   cluster path_query(vertex_index a, vertex_index b) {
     soft_expose(a, b);
     node_index r = v[a].hn;
     if(n[r].v[0] == a && n[r].v[1] == b) return n[r].f;
     if(n[r].v[0] == a) return n[r][0].f;
     if(n[r].v[1] == b) return n[r][1].f;
     push(n[r].c[1]);
     return n[r][1][0].f;
   }

   void bring(node_index r, int dir) {
     node_index i = n[r].c[2];
     if(!i) {
       i = n[r].c[dir ^ 1];
       n[i].c[3] = 0;
       fix(i);
     }
     else if(n[i].ty == type::Rake) {
       while(push(i), n[i][1].ty == type::Rake) i = n[i].c[1];
       splay(i);
       n[n[i][0].c[3] = r].c[2] = n[i].c[0];
       if(dir) reverse(n[i].c[1]);
       n[n[i][1].c[3] = r].c[dir] = n[i].c[1];
       fix(n[r].c[2]); fix(n[r].c[dir]); fix(r);
     }
     else {
       if(dir) reverse(i);
       n[n[i].c[3] = r].c[dir] = i;
       n[r].c[2] = 0;
       fix(n[r].c[dir]); fix(r);
     }
   }

   void cut(vertex_index a, vertex_index b) {
     soft_expose(a, b);
     node_index r = v[a].hn;
     node_index s = v[b].hn;
     push(s);
     n[s].c[3] = 0;
     n[r].c[1] = 0;
     bring(r, 1);
     bring(s, 0);
   }

   int all_tree(vertex_index a) {
     expose(a);
     return n[v[a].hn].f.length + n[v[a].hn](0).val + n[v[a].hn](1).val;
   }
}

Union Find

Union Find

Spec

  • (constructor)

    • n要素のUnion Findを構築する.
  • root

    • 要素の根を返す.
  • unite

    • 2要素を結ぶ.
    • 戻り値は親となった根

Code

#include <vector>
#include <tuple>

struct union_find {
  std::vector<int> par;
  union_find(int N): par(N, -1) {}
  int root(int x) {
    return par[x] < 0 ? x : par[x] = root(par[x]);
  }
  std::tuple<int, int> unite(int x, int y) {
    x = root(x);
    y = root(y);
    if(x == y) return { -1, -1 };
    if(par[x] > par[y]) std::swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return { x, y };
  }
  int size(int x) {
    return -par[root(x)];
  }
};

Partially Persistent Union Find

Spec

  • 時刻tは単調増加である必要がある.

  • (constructor)

    • n要素のPartially Persistent Union Findを構築する.
  • unite(i64 t, i64 x, i64 y)

    • 2要素を時刻tで結ぶ.
    • 戻り値は親となった根
  • find(i64 t, i64 x)

    • 時刻tのときの要素xの根を返す.
  • size(i64 t, i64 x)

    • 時刻tのときの要素xの属する集合の大きさを返す.

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct partially_persistent_union_find {
  vector<i64> data;
  vector<i64> last;
  vector<vector<pair<i64, i64>>> add;

  partially_persistent_union_find(int sz) : data(sz, -1), last(sz, 1e9), add(sz) {
    for(auto & vs: add) vs.push_back({ -1, -1 });
  }

  i64 unite(i64 t, i64 x, i64 y) {
    x = find(t, x);
    y = find(t, y);
    if(x == y) return -1;
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    add[x].push_back({t, data[x]});
    data[y] = x;
    last[y] = t;
    return x;
  }

  i64 find(i64 t, i64 x) {
    if(t < last[x]) return x;
    return find(t, data[x]);
  }

  i64 size(i64 t, i64 x) {
    x = find(t, x);
    return -prev(lower_bound(begin(add[x]), end(add[x]), make_pair(t, 0ll)))->second;
  }
};

self-Balancing Binary Search Tree

Splay Tree Array

列を管理するSplay Tree

Spec

  • struct node

    • Splay Treeに載せるノードの型
  • fold(node* x)

    • foldしたいやつを変えたときはここを変える
  • fix(node* n)

    • fix操作, foldの仕方をここで定義する
  • reverse(node* n)

    • 反転操作, foldを反転する必要がある場合はここに.
  • push(node* x)

    • 遅延伝搬させているときはここを変更
  • split(i64 i)

    • [0, i) / [i, ..)に分ける
  • merge(splay_array&& arr)

    • mergeする
  • reverse()

    • 列全体を反転させる
  • update(i64 i, T t)

    • i番目の要素をtに変更する.

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct splay_array {
  using T = i64;

  struct node {
    node* ch[2];
    node* par;
    T val;
    i64 sz;
    bool rev;

    /* option */
    T fold;
    
    node(T v): val(v), par(nullptr), sz(1), rev(false), fold(v) { ch[0] = nullptr; ch[1] = nullptr; }
  };

private:

  i64 subsize(node* x) {
    if(x) return x->sz;
    else return 0;
  }
  T fold(node* x) {
    if(x) return x->fold;
    else return 0;
  }
  node* fix(node* n) {
    if(!n) return nullptr;
    n->sz = subsize(n->ch[0]) + subsize(n->ch[1]) + 1;
    /* option */
    n->fold = fold(n->ch[0]) + n->val + fold(n->ch[1]);
    return n;
  }
  void reverse(node* n) {
    if(!n) return;
    n->rev ^= true;
    /* here reversing operation */
    /* example swap(fold, revfold) */
  }
  void push(node* x) {
    if(!x) return;
    if(x->rev) {
      swap(x->ch[0], x->ch[1]);
      reverse(x->ch[0]);
      reverse(x->ch[1]);
    }
  }
  int parent_dir(node* x) {
    node* p = x->par;
    if(!p) return -1;
    else if(p->ch[0] == x) return 0;
    else return 1;
  }
  void set(node* par, node* x, i64 dir) {
    if(par) par->ch[dir] = x;
    if(x) x->par = par;
    fix(par);
  }
  void rotate(node* x, i64 dir) {
    node* p = x->par;
    node* q = p->par;
    set(p, x->ch[dir], dir ^ 1);
    int p_dir = parent_dir(p);
    if(p_dir >= 0) {
      set(q, x, p_dir);
    }
    else x->par = nullptr;
    set(x, p, dir);
  }
  node* splay(node * x) {
    if(!x) return nullptr;
    while(x->par) {
      push(x->par->par);
      push(x->par);
      push(x);
      int dir = parent_dir(x);
      int eir = parent_dir(x->par);
      if(eir == -1) {
        rotate(x, dir  ^ 1);
      }
      else if(dir == eir){
        rotate(x->par, eir ^ 1);
        rotate(x, dir ^ 1);
      }
      else {
        rotate(x, dir ^ 1);
        rotate(x, eir ^ 1);
      }
    }
    return x;
  }

  node* find(node* r, i64 i) {
    push(r);
    assert(0 <= i);
    assert(i < subsize(r));
    node* z = r;
    while(z) {
      push(z);
      if(subsize(z->ch[0]) == i) return splay(z);
      else if(subsize(z->ch[0]) < i) {
        i -= subsize(z->ch[0]) + 1;
        z = z->ch[1];
      }
      else {
        z = z->ch[0];
      }
    }
    assert(false);
  }

  pair<node*, node*> split(node* r, size_t i) {
    push(r);
    assert(0 <= i);
    assert(i <= subsize(r));
    if(i == 0) return { nullptr, r };
    if(i == subsize(r)) return { r, nullptr };
    r = find(r, i - 1);
    node* y = r->ch[1];
    if(y) y->par = nullptr;
    r->ch[1] = nullptr;
    fix(r);
    push(y);
    return { r, y };
  }

  node* merge(node* r1, node* r2) {
    push(r1);
    push(r2);
    if(!r1) r1 = r2;
    else if(!r2) {}
    else {
      r1 = find(r1, subsize(r1) - 1);
      set(r1, r2, 1);
    }
    return r1;
  }

  node* root;

  splay_array(node* r): root(r) {}

public:

  using sarr = splay_array;

  splay_array(): root(nullptr) {}
  splay_array(T t): root(new node(t)) {}
  splay_array(splay_array&& arr): root(arr.root) { arr.root = nullptr; }
  splay_array& operator=(splay_array&& arr) {
    root = arr.root;
    arr.root = nullptr;
    return *this;
  }
  /* [0 ... i - 1] +/+ [i ...] */
  pair<splay_array, splay_array> split(i64 i) {
    auto p = split(root, i);
    root = nullptr;
    return { splay_array(p.first), splay_array(p.second) };
  }
  /* [this] ++ [arr] */
  void merge(splay_array&& arr) {
    root = merge(root, arr.root);
    arr.root = nullptr;
  }
  /* reverse array */
  void reverse() { if(root) reverse(root); }
  i64 size() { return subsize(root); }

  /* option */
  T fold() { return fold(root); }
  void update(i64 i, T t) {
    root = find(root, i);
    root->val += t;
    fix(root);
  }
};

Splay Tree Map

map型のSplay Tree

Spec

  • struct node

    • Splay Treeに載せるノードの型
  • fold(node* x)

    • foldしたいやつを変えたときはここを変える
  • fix(node* n)

    • fix操作, foldの仕方をここで定義する
  • reverse(node* n)

    • 反転操作, foldを反転する必要がある場合はここに.
  • push(node* x)

    • 遅延伝搬させているときはここを変更
  • insert(Key key, T t)

    • {key, t}を入れる, すでに存在する場合はupdate
  • erase(Key key)

    • `{key, ..}のノードを削除する
  • nth_node(i64 n)

    • n番目の要素のKeyを返す

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct splay_map {
  using T = i64;
  using Key = i64;

  struct node {
    node* ch[2];
    node* par;
    T val;
    Key key;
    i64 sz;
    bool rev;

    /* option */
    //T fold;
    
    node(Key key, T v): key(key), val(v), par(nullptr), sz(1), rev(false)/*, fold(v)*/ { ch[0] = nullptr; ch[1] = nullptr; }
  };

private:

  i64 subsize(node* x) {
    if(x) return x->sz;
    else return 0;
  }
  /*
  T fold(node* x) {
    if(x) return x->fold;
    else return 0;
  }
  */
  node* fix(node* n) {
    if(!n) return nullptr;
    n->sz = subsize(n->ch[0]) + subsize(n->ch[1]) + 1;
    /* option */
    //n->fold = fold(n->ch[0]) + n->val + fold(n->ch[1]);
    return n;
  }
  void reverse(node* n) {
    if(!n) return;
    n->rev ^= true;
    /* here reversing operation */
    /* example swap(fold, revfold) */
  }
  void push(node* x) {
    if(!x) return;
    if(x->rev) {
      swap(x->ch[0], x->ch[1]);
      reverse(x->ch[0]);
      reverse(x->ch[1]);
    }
  }
  int parent_dir(node* x) {
    node* p = x->par;
    if(!p) return -1;
    else if(p->ch[0] == x) return 0;
    else return 1;
  }
  void set(node* par, node* x, i64 dir) {
    if(par) par->ch[dir] = x;
    if(x) x->par = par;
    fix(par);
  }
  void rotate(node* x, i64 dir) {
    node* p = x->par;
    node* q = p->par;
    set(p, x->ch[dir], dir ^ 1);
    int p_dir = parent_dir(p);
    if(p_dir >= 0) {
      set(q, x, p_dir);
    }
    else x->par = nullptr;
    set(x, p, dir);
  }
  node* splay(node * x) {
    if(!x) return nullptr;
    while(x->par) {
      push(x->par->par);
      push(x->par);
      push(x);
      int dir = parent_dir(x);
      int eir = parent_dir(x->par);
      if(eir == -1) {
        rotate(x, dir  ^ 1);
      }
      else if(dir == eir){
        rotate(x->par, eir ^ 1);
        rotate(x, dir ^ 1);
      }
      else {
        rotate(x, dir ^ 1);
        rotate(x, eir ^ 1);
      }
    }
    return x;
  }

  node* root;

public:

  splay_map(): root(nullptr) {}

  node* lower_bound(Key key) {
    node* z = root;
    node* x = nullptr;
    while(z) {
      if(key <= z->key) {
        x = z;
        z = z->ch[0];
      }
      else {
        z = z->ch[1];
      }
    }
    return x;
  }

  void insert(Key key, T t) {
    node* z = new node(key, t);
    node* x = lower_bound(key);
    if(!x) {
      set(z, root, 0);
      root = z;
    }
    else {
      root = splay(x);
      node* q = root->ch[0];
      set(z, q, 0);
      set(root, z, 0);
    }
  }

  void erase(Key key) {
    node* x = lower_bound(key);
    if(x->key == key) {
      root = splay(x);
      node* p = root->ch[0];
      node* q = root->ch[1];
      root->ch[0] = nullptr;
      root->ch[1] = nullptr;
      if(p) p->par = nullptr;
      if(q) q->par = nullptr;
      if(!p) {
        root = q;
      }
      else {
        while(p->ch[1]) p = p->ch[1];
        root = splay(p);
        set(root, q, 1);
      }
    }
  }

  i64 size() {
    return subsize(root);
  }
  Key nth_node(i64 n) {
    node* z = root;
    while(z) {
      if(subsize(z->ch[0]) == n) {
        return z->key;
      }
      if(subsize(z->ch[0]) < n) {
        n -= subsize(z->ch[0]) + 1;
        z = z->ch[1];
      }
      else {
        z = z->ch[0];
      }
    }
    assert(false);
  }
  

  /* option */
  //T fold() { return fold(root); }
};

AVL Tree Array

列を管理するAVL Tree

Spec

  • struct node

    • AVL Treeに載せるノードの型
    • fixに操作を書く
  • split(size_type i)

    • [0, i) / [i, ..)に分けた木を返す
  • merge(persistent_avl_array&& arr)

    • mergeした木を返す.
  • at(size_type i)

    • i番目の要素にアクセスする.
  • set(size_type i, value_type val)

    • i番目の要素をvalにする

Code

#include <memory>
#include <array>

template<class T>
class avl_tree_array {
  public:
    using value_type = T;
    using size_type = std::size_t;
    using height_type = long long int;
  protected:
    class node;
    using node_type = std::unique_ptr<class node>;

    static size_type size(const node_type& node) {
      if(!node) return 0;
      else return node->size();
    }
    static height_type height(const node_type& node) {
      if(!node) return 0;
      else return node->height();
    }

    class node {
      private:
        value_type val;
        size_type sz;
        height_type hei;
        std::array<node_type, 2> chi;
      public:

        node(value_type val): val(std::move(val)), sz(1), hei(1), chi() { fix(); }
        void fix() {
          sz = avl_tree_array::size(chi[0]) + avl_tree_array::size(chi[1]) + 1;
          hei = std::max(avl_tree_array::height(chi[0]), avl_tree_array::height(chi[1])) + 1;
        }
        void push() {}
        node_type cut(size_type dir) {
          push();
          node_type nn = std::move(chi[dir]);
          this->fix();
          return std::move(nn);
        }
        void set(node_type dir_node, size_type dir) {
          push();
          chi[dir] = std::move(dir_node);
          this->fix();
        }
        size_type size() { push(); return sz; }
        height_type height() { push(); return hei; }
        height_type diff() { push(); return avl_tree_array::height(chi[0]) - avl_tree_array::height(chi[1]); }
        value_type& value() { push(); return val; }
        node_type& child(size_type dir) { return chi[dir]; }
    };


    static node_type rotate(node_type x, size_type dir) {
      node_type y = x->cut(1 - dir);
      node_type b = y->cut(dir);
      x->set(std::move(b), 1 - dir);
      y->set(std::move(x), dir);
      return std::move(y);
    }

    static node_type balance(node_type node) {
      if(node->diff() == 2) {
        if(node->child(0)->diff() == -1) {
          auto ch = node->cut(0);
          node->set(rotate(std::move(ch), 0), 0);
        }
        return rotate(std::move(node), 1);
      }
      else if(node->diff() == -2) {
        if(node->child(1)->diff() == 1) {
          auto ch = node->cut(1);
          node->set(rotate(std::move(ch), 1), 1);
        }
        return rotate(std::move(node), 0);
      }
      else return std::move(node);
    }

    static std::pair<node_type, node_type> deepest_node(node_type node, size_type dir) {
      auto ch = node->cut(dir);
      if(ch) {
        auto pp = deepest_node(std::move(ch), dir);
        node_type deepest_node, dirn;
        deepest_node = std::move(pp.first);
        dirn = std::move(pp.second);
        node->set(std::move(dirn), dir);
        pp.first = std::move(deepest_node);
        pp.second = balance(std::move(node));
        return std::move(pp);
      }
      else {
        auto rn = node->cut(1 - dir);
        std::pair<node_type, node_type> pp;
        pp.first = std::move(node);
        pp.second = std::move(rn);
        return pp;
      }
    }

    static node_type merge_dir(node_type dst, node_type root, node_type src, size_type dir) {
      if(std::abs(height(dst) - height(src)) <= 1) {
        root->set(std::move(src), dir);
        root->set(std::move(dst), 1 - dir);
        return std::move(root);
      }
      else {
        node_type ch = dst->cut(dir);
        if(ch) {
          ch = merge_dir(std::move(ch), std::move(root), std::move(src), dir);
          dst->set(std::move(ch), dir);
          return balance(std::move(dst));
        }
        else {
          root->set(std::move(src), dir);
          root = balance(std::move(root));
          dst->set(std::move(root), dir);
          return balance(std::move(dst));
        }
      }
    }

    static node_type merge(node_type left, node_type right) {
      if(!left) { return std::move(right); }
      else if(!right) { return std::move(left); }
      else if(height(left) >= height(right)) {
        node_type deep_left, src;
        std::tie(deep_left, src) = deepest_node(std::move(right), 0);
        return merge_dir(std::move(left), std::move(deep_left), std::move(src), 1);
      }
      else {
        node_type deep_right, src;
        std::tie(deep_right, src) = deepest_node(std::move(left), 1);
        return merge_dir(std::move(right), std::move(deep_right), std::move(src), 0);
      }
    }

    static std::pair<node_type, node_type> split(node_type node, size_type i) {
      if(i == node->size()) { return std::pair<node_type, node_type>(std::move(node), node_type()); }
      auto left = node->cut(0);
      auto right = node->cut(1);
      if(i < size(left)) {
        node_type sp_left, sp_right;
        std::tie(sp_left, sp_right) = split(std::move(left), i);
        node_type nright;
        if(right) {
          nright = merge_dir(std::move(right), std::move(node), std::move(sp_right), 0);
        }
        else {
          nright = merge(std::move(sp_right), std::move(node));
        }
        return std::pair<node_type, node_type>(std::move(sp_left), std::move(nright));
      }
      else if(i == size(left)) {
        return std::pair<node_type, node_type>(std::move(left), merge(std::move(node), std::move(right)));
      }
      else {
        node_type sp_left, sp_right;
        std::tie(sp_left, sp_right) = split(std::move(right), i - size(left) - 1);
        node_type nleft;
        if(left) {
          nleft = merge_dir(std::move(left), std::move(node), std::move(sp_left), 1);
        }
        else {
          nleft = merge(std::move(node), std::move(sp_left));
        }
        return std::pair<node_type, node_type>(std::move(nleft), std::move(sp_right));
      }
    }

    static node_type& at(node_type& node, size_type i) {
      if(size(node->child(0)) == i) return node;
      else if(size(node->child(0)) < i) return at(node->child(1), i - size(node->child(0)) - 1);
      else return at(node->child(0), i);
    }

    static void set(node_type& node, size_type i, value_type val) {
      if(size(node->child(0)) == i) {
        node->value() = std::move(val);
        node->fix();
      }
      else if(size(node->child(0)) < i) {
        set(node->child(1), i - size(node->child(0)) - 1, std::move(val));
        node->fix();
      }
      else {
        set(node->child(0), i, std::move(val));
        node->fix();
      }
    }

    node_type root;
    avl_tree_array(node_type&& root): root(std::move(root)) {}
  public:
    avl_tree_array(): root() {}
    avl_tree_array(T val): root(node_type(new class node(std::move(val)))) {}
    avl_tree_array(avl_tree_array&& tree): root(std::move(tree.root)) {}
    avl_tree_array& operator=(avl_tree_array&& tree) {
      root = std::move(tree.root);
      return *this;
    }
    template<class A>
      friend avl_tree_array<A> merge(avl_tree_array<A>&& t1, avl_tree_array<A>&& t2);
    template<class A>
      friend std::pair<avl_tree_array<A>, avl_tree_array<A>> split(avl_tree_array<A>&& t, std::size_t i);

    value_type& at(size_type i) {
      return at(root, i)->value();
    }

    void set(size_type i, value_type val) {
      set(root, i, std::move(val));
    }

    size_type size() {
      if(!root) return 0;
      return root->size();
    }
};

template<class T>
avl_tree_array<T> merge(avl_tree_array<T>&& t1, avl_tree_array<T>&& t2) {
  return avl_tree_array<T>(avl_tree_array<T>::merge(std::move(t1.root), std::move(t2.root)));
}

template<class T>
std::pair<avl_tree_array<T>, avl_tree_array<T>> split(avl_tree_array<T>&& t, std::size_t i) {
  auto rp = avl_tree_array<T>::split(std::move(t.root), i);
  return std::make_pair(avl_tree_array<T>(std::move(rp.first)), avl_tree_array<T>(std::move(rp.second)));
}

AVL Tree Array Tuned

Code

#include <utility>
#include <cstdint>
#include <algorithm>
#include <string>
#include <iostream>

struct avl_tree_array {
  using value_type = long long;
  using size_type = std::size_t;
  using height_type = std::int_least32_t;
  using node_index = std::int_least32_t;

  struct node;

  static struct node n[501010];
  static size_type ni;

  struct node {
    value_type val;
    size_type s;
    height_type h;
    node_index c[2];
    value_type f;
    node() : f(0) {}
    node& operator[](size_type d) { return n[c[d]]; }
  };

  static node_index new_node(value_type v) {
    node_index i = ni++;
    n[i].val = v;
    fix(i);
    return i;
  }

  static void fix(node_index i) {
    n[i].s = n[i][0].s + 1 + n[i][1].s;
    n[i].h = std::max(n[i][0].h, n[i][1].h) + 1;
    n[i].f = n[i][0].f + n[i].val + n[i][1].f;
  }

  static void rotate(node_index& x, size_type dir) {
    node_index y = n[x].c[1 ^ dir];
    n[x].c[1 ^ dir] = n[y].c[dir];
    n[y].c[dir] = x;
    fix(x);
    fix(y);
    x = y;
  }

  static void balance(node_index& i) {
    fix(i);
    if(n[i][0].h - n[i][1].h == 2) {
      if(n[i][0][0].h - n[i][0][1].h == -1) {
        rotate(n[i].c[0], 0);
      }
      rotate(i, 1);
    }
    else if(n[i][0].h - n[i][1].h == -2) {
      if(n[i][1][0].h - n[i][1][1].h == 1) {
        rotate(n[i].c[1], 1);
      }
      rotate(i, 0);
    }
  }

  static std::pair<node_index, node_index> deepest_node(node_index i, size_type dir) {
    node_index par = -1;
    while(n[i].c[dir]) {
      std::swap(par, n[i].c[dir]);
      std::swap(par, i);
    }
    node_index ln = n[i].c[dir ^ 1];
    n[i].c[dir ^ 1] = 0;
    node_index dn = i;
    fix(dn);
    i = ln;
    while(std::swap(par, i), i > 0) {
      std::swap(par, n[i].c[dir]);
      balance(i);
    }
    return std::pair<node_index, node_index>(par, dn);
  }

  static node_index merge_dir(node_index l, node_index root, node_index r, size_type dir) {
    node_index par = -1;
    while(std::abs(n[l].h - n[r].h) > 1) {
      std::swap(par, n[l].c[dir]);
      std::swap(par, l);
    }
    n[root].c[dir ^ 1] = l;
    n[root].c[dir] = r;
    fix(root);
    l = root;
    while(std::swap(par, l), l > 0) {
      std::swap(par, n[l].c[dir]);
      balance(l);
    }
    return par;
  }

  static node_index merge(node_index l, node_index r) {
    if(!l) return r;
    else if(!r) return l;
    else if(n[l].h >= n[r].h) {
      auto p = deepest_node(r, 0);
      return merge_dir(l, p.second, p.first, 1);
    }
    else {
      auto p = deepest_node(l, 1);
      return merge_dir(r, p.second, p.first, 0);
    }
  }

  static std::pair<node_index, node_index> split(node_index root, size_type pos) {
    if(pos == n[root].s) return { root, 0 };
    node_index i = root;
    node_index par = -1;
    while(i > 0 && pos != n[i][0].s) {
      if(pos < n[i][0].s) {
        std::swap(par, n[i].c[0]);
      }
      else {
        pos -= n[i][0].s + 1;
        std::swap(par, n[i].c[1]);
      }
      std::swap(par, i);
    }

    node_index l = n[i].c[0];
    node_index r = merge_dir(n[i].c[1], i, 0, 0);

    while(std::swap(par, i), i > 0) {
      if(n[i].c[0] == -1 || n[i].h < n[i][0].h) {
        par = n[i].c[0];
        r = merge_dir(n[i].c[1], i, r, 0);
      }
      else {
        par = n[i].c[1];
        l = merge_dir(n[i].c[0], i, l, 1);
      }
    }
    return { l, r };
  }

  static node_index at(node_index i, size_type pos) {
    while(pos != n[i][0].s) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s + 1;
        i = n[i].c[1];
      }
    }
    return i;
  }

  static void set(node_index i, size_type pos, value_type v) {
    node_index par = -1;
    while(pos != n[i][0].s) {
      if(pos < n[i][0].s) {
        std::swap(par, n[i].c[0]);
      }
      else {
        pos -= n[i][0].s + 1;
        std::swap(par, n[i].c[1]);
      }
      std::swap(par, i);
    }
    n[i].val = v;
    fix(i);

    while(std::swap(par, i), i > 0) {
      if(n[i].c[0] == -1 || n[i].h < n[i][0].h) {
        std::swap(par, n[i].c[0]);
      }
      else {
        std::swap(par, n[i].c[1]);
      }
      fix(i);
    }
  }

  static void debug(node_index i, std::string s) {
    if(i == 0) {
      std::cout << s << 0 << std::endl;
      return;
    }
    std::cout << s << i << " = " << n[i].val << " = " << n[i].h << std::endl;
    debug(n[i].c[0], s + "  ");
    debug(n[i].c[1], s + "  ");
  }

public:

  node_index root;
  avl_tree_array(): root(0) {}
  avl_tree_array(value_type val): root(new_node(val)) {}

  avl_tree_array& merge(avl_tree_array right) {
    root = merge(this->root, right.root);
    return *this;
  }
  avl_tree_array split(size_type i) {
    auto p = split(root, i);
    avl_tree_array avl;
    avl.root = p.second;
    root = p.first;
    return avl;
  }

  void set(size_type i, value_type v) {
    set(root, i, v);
  }

  const value_type& at(size_type i) {
    return n[at(root, i)].val;
  }

  value_type fold() {
    return n[root].f;
  }

  size_type size() {
    return n[root].s;
  }

  void debug() {
    debug(root, "");
  }
};

avl_tree_array::node avl_tree_array::n[501010];
avl_tree_array::size_type avl_tree_array::ni = 1;

AVL Tree Map

AVL TreeのMapバージョン

Spec

  • struct node

    • AVL Treeに載せるノードの型
    • fixに操作を書く.
  • at(key_value i)

    • iの要素にアクセスする.
  • insert(key_value key, value_type val)

    • keyの要素をvalにする
  • erase(key_value key)

    • keyの要素を消す
  • lower_bound(key_value key)

    • lower_boundをしたときの要素を返す.
    • .firstfalseであるときはlower_boundはない.
  • nth_node(size_type i)

    • i番目の要素を返す.

Code

#include <memory>
#include <array>

template<class T>
class avl_tree_array {
  public:
    using value_type = T;
    using size_type = std::size_t;
    using height_type = long long int;
  protected:
    class node;
    using node_type = std::unique_ptr<class node>;

    static size_type size(const node_type& node) {
      if(!node) return 0;
      else return node->size();
    }
    static height_type height(const node_type& node) {
      if(!node) return 0;
      else return node->height();
    }

    class node {
      private:
        value_type val;
        size_type sz;
        height_type hei;
        std::array<node_type, 2> chi;
      public:

        node(value_type val): val(std::move(val)), sz(1), hei(1), chi() { fix(); }
        void fix() {
          sz = avl_tree_array::size(chi[0]) + avl_tree_array::size(chi[1]) + 1;
          hei = std::max(avl_tree_array::height(chi[0]), avl_tree_array::height(chi[1])) + 1;
        }
        void push() {}
        node_type cut(size_type dir) {
          push();
          node_type nn = std::move(chi[dir]);
          this->fix();
          return std::move(nn);
        }
        void set(node_type dir_node, size_type dir) {
          push();
          chi[dir] = std::move(dir_node);
          this->fix();
        }
        size_type size() { push(); return sz; }
        height_type height() { push(); return hei; }
        height_type diff() { push(); return avl_tree_array::height(chi[0]) - avl_tree_array::height(chi[1]); }
        value_type& value() { push(); return val; }
        node_type& child(size_type dir) { return chi[dir]; }
    };


    static node_type rotate(node_type x, size_type dir) {
      node_type y = x->cut(1 - dir);
      node_type b = y->cut(dir);
      x->set(std::move(b), 1 - dir);
      y->set(std::move(x), dir);
      return std::move(y);
    }

    static node_type balance(node_type node) {
      if(node->diff() == 2) {
        if(node->child(0)->diff() == -1) {
          auto ch = node->cut(0);
          node->set(rotate(std::move(ch), 0), 0);
        }
        return rotate(std::move(node), 1);
      }
      else if(node->diff() == -2) {
        if(node->child(1)->diff() == 1) {
          auto ch = node->cut(1);
          node->set(rotate(std::move(ch), 1), 1);
        }
        return rotate(std::move(node), 0);
      }
      else return std::move(node);
    }

    static std::pair<node_type, node_type> deepest_node(node_type node, size_type dir) {
      auto ch = node->cut(dir);
      if(ch) {
        auto pp = deepest_node(std::move(ch), dir);
        node_type deepest_node, dirn;
        deepest_node = std::move(pp.first);
        dirn = std::move(pp.second);
        node->set(std::move(dirn), dir);
        pp.first = std::move(deepest_node);
        pp.second = balance(std::move(node));
        return std::move(pp);
      }
      else {
        auto rn = node->cut(1 - dir);
        std::pair<node_type, node_type> pp;
        pp.first = std::move(node);
        pp.second = std::move(rn);
        return pp;
      }
    }

    static node_type merge_dir(node_type dst, node_type root, node_type src, size_type dir) {
      if(std::abs(height(dst) - height(src)) <= 1) {
        root->set(std::move(src), dir);
        root->set(std::move(dst), 1 - dir);
        return std::move(root);
      }
      else {
        node_type ch = dst->cut(dir);
        if(ch) {
          ch = merge_dir(std::move(ch), std::move(root), std::move(src), dir);
          dst->set(std::move(ch), dir);
          return balance(std::move(dst));
        }
        else {
          root->set(std::move(src), dir);
          root = balance(std::move(root));
          dst->set(std::move(root), dir);
          return balance(std::move(dst));
        }
      }
    }

    static node_type merge(node_type left, node_type right) {
      if(!left) { return std::move(right); }
      else if(!right) { return std::move(left); }
      else if(height(left) >= height(right)) {
        node_type deep_left, src;
        std::tie(deep_left, src) = deepest_node(std::move(right), 0);
        return merge_dir(std::move(left), std::move(deep_left), std::move(src), 1);
      }
      else {
        node_type deep_right, src;
        std::tie(deep_right, src) = deepest_node(std::move(left), 1);
        return merge_dir(std::move(right), std::move(deep_right), std::move(src), 0);
      }
    }

    static std::pair<node_type, node_type> split(node_type node, size_type i) {
      if(i == node->size()) { return std::pair<node_type, node_type>(std::move(node), node_type()); }
      auto left = node->cut(0);
      auto right = node->cut(1);
      if(i < size(left)) {
        node_type sp_left, sp_right;
        std::tie(sp_left, sp_right) = split(std::move(left), i);
        node_type nright;
        if(right) {
          nright = merge_dir(std::move(right), std::move(node), std::move(sp_right), 0);
        }
        else {
          nright = merge(std::move(sp_right), std::move(node));
        }
        return std::pair<node_type, node_type>(std::move(sp_left), std::move(nright));
      }
      else if(i == size(left)) {
        return std::pair<node_type, node_type>(std::move(left), merge(std::move(node), std::move(right)));
      }
      else {
        node_type sp_left, sp_right;
        std::tie(sp_left, sp_right) = split(std::move(right), i - size(left) - 1);
        node_type nleft;
        if(left) {
          nleft = merge_dir(std::move(left), std::move(node), std::move(sp_left), 1);
        }
        else {
          nleft = merge(std::move(node), std::move(sp_left));
        }
        return std::pair<node_type, node_type>(std::move(nleft), std::move(sp_right));
      }
    }

    static node_type& at(node_type& node, size_type i) {
      if(size(node->child(0)) == i) return node;
      else if(size(node->child(0)) < i) return at(node->child(1), i - size(node->child(0)) - 1);
      else return at(node->child(0), i);
    }

    static void set(node_type& node, size_type i, value_type val) {
      if(size(node->child(0)) == i) {
        node->value() = std::move(val);
        node->fix();
      }
      else if(size(node->child(0)) < i) {
        set(node->child(1), i - size(node->child(0)) - 1, std::move(val));
        node->fix();
      }
      else {
        set(node->child(0), i, std::move(val));
        node->fix();
      }
    }

    node_type root;
    avl_tree_array(node_type&& root): root(std::move(root)) {}
  public:
    avl_tree_array(): root() {}
    avl_tree_array(T val): root(node_type(new class node(std::move(val)))) {}
    avl_tree_array(avl_tree_array&& tree): root(std::move(tree.root)) {}
    avl_tree_array& operator=(avl_tree_array&& tree) {
      root = std::move(tree.root);
      return *this;
    }
    template<class A>
      friend avl_tree_array<A> merge(avl_tree_array<A>&& t1, avl_tree_array<A>&& t2);
    template<class A>
      friend std::pair<avl_tree_array<A>, avl_tree_array<A>> split(avl_tree_array<A>&& t, std::size_t i);

    value_type& at(size_type i) {
      return at(root, i)->value();
    }

    void set(size_type i, value_type val) {
      set(root, i, std::move(val));
    }

    size_type size() {
      if(!root) return 0;
      return root->size();
    }
};

template<class T>
avl_tree_array<T> merge(avl_tree_array<T>&& t1, avl_tree_array<T>&& t2) {
  return avl_tree_array<T>(avl_tree_array<T>::merge(std::move(t1.root), std::move(t2.root)));
}

template<class T>
std::pair<avl_tree_array<T>, avl_tree_array<T>> split(avl_tree_array<T>&& t, std::size_t i) {
  auto rp = avl_tree_array<T>::split(std::move(t.root), i);
  return std::make_pair(avl_tree_array<T>(std::move(rp.first)), avl_tree_array<T>(std::move(rp.second)));
}

AVL Tree Map Foldable

AVL TreeのMapバージョンでモノイドを処理できるようにした.

Spec

  • operation()

    • モノイドの演算を決める
  • identity()

    • モノイドの単位元を決める
  • struct node

    • AVL Treeに載せるノードの型
    • fixに操作を書く.
  • at(key_value i)

    • iの要素にアクセスする.
  • insert(key_value key, value_type val)

    • keyの要素をvalにする
  • erase(key_value key)

    • keyの要素を消す
  • lower_bound(key_value key)

    • lower_boundをしたときの要素を返す.
    • .firstfalseであるときはlower_boundはない.
  • nth_node(size_type i)

    • i番目の要素を返す.
  • fold(key_value left, key_value right)

    • [left, right)に対してfoldをする.

Code

#include <memory>
#include <array>
#include <cassert>
#include <iostream>

template<class Key, class Monoid>
class avl_tree_map {
  public:
    using key_type = Key;
    using value_type = Monoid;
    using size_type = std::size_t;
    using height_type = long long int;
  protected:
    class node;
    using node_type = std::unique_ptr<class node>;

    static size_type size(const node_type& node) {
      if(!node) return 0;
      else return node->size();
    }
    static height_type height(const node_type& node) {
      if(!node) return 0;
      else return node->height();
    }

    static value_type identity() {
      return 0;
    }

    static value_type operation(const value_type& a, const value_type& b) {
      return a + b;
    }

    class node {
      private:
        key_type ky;
        value_type val;
        size_type sz;
        height_type hei;
        std::array<node_type, 2> chi;
      public:

        value_type fold;

        node(key_type key, value_type val): ky(std::move(key)), val(std::move(val)), fold(identity()), sz(1), hei(1), chi() { fix(); }
        void fix() {
          fold = val;
          if(chi[0]) fold = operation(chi[0]->fold, fold);
          if(chi[1]) fold = operation(fold, chi[1]->fold);
          sz = avl_tree_map::size(chi[0]) + avl_tree_map::size(chi[1]) + 1;
          hei = std::max(avl_tree_map::height(chi[0]), avl_tree_map::height(chi[1])) + 1;
        }
        void push() {}
        node_type cut(size_type dir) {
          push();
          node_type nn = std::move(chi[dir]);
          this->fix();
          return std::move(nn);
        }
        void set(node_type dir_node, size_type dir) {
          push();
          chi[dir] = std::move(dir_node);
          this->fix();
        }
        size_type size() { push(); return sz; }
        height_type height() { push(); return hei; }
        height_type diff() { push(); return avl_tree_map::height(chi[0]) - avl_tree_map::height(chi[1]); }
        value_type& value() { push(); return val; }
        const key_type& key() { push(); return ky; }
        node_type& child(size_type dir) { return chi[dir]; }
    };


    static node_type rotate(node_type x, size_type dir) {
      node_type y = x->cut(1 - dir);
      node_type b = y->cut(dir);
      x->set(std::move(b), 1 - dir);
      y->set(std::move(x), dir);
      return std::move(y);
    }

    static node_type balance(node_type node) {
      if(node->diff() == 2) {
        if(node->child(0)->diff() == -1) {
          auto ch = node->cut(0);
          node->set(rotate(std::move(ch), 0), 0);
        }
        return rotate(std::move(node), 1);
      }
      else if(node->diff() == -2) {
        if(node->child(1)->diff() == 1) {
          auto ch = node->cut(1);
          node->set(rotate(std::move(ch), 1), 1);
        }
        return rotate(std::move(node), 0);
      }
      else return std::move(node);
    }

    static node_type insert(node_type node, key_type key, value_type val) {
      if(!node) {
        return node_type(new class node(std::move(key), std::move(val)));
      }
      else if(node->key() == key) {
        node->value() = val;
        node->fix();
        return std::move(node);
      }
      else if(node->key() < key) {
        auto right = node->cut(1);
        node->set(insert(std::move(right), std::move(key), std::move(val)), 1);
        return balance(std::move(node));
      }
      else {
        auto left = node->cut(0);
        node->set(insert(std::move(left), std::move(key), std::move(val)), 0);
        return balance(std::move(node));
      }
    }

    static std::pair<node_type, node_type> deepest_node(node_type node, size_type dir) {
      auto ch = node->cut(dir);
      if(ch) {
        auto pp = deepest_node(std::move(ch), dir);
        node_type deepest_node, dirn;
        deepest_node = std::move(pp.first);
        dirn = std::move(pp.second);
        node->set(std::move(dirn), dir);
        pp.first = std::move(deepest_node);
        pp.second = balance(std::move(node));
        return std::move(pp);
      }
      else {
        auto rn = node->cut(1 - dir);
        std::pair<node_type, node_type> pp;
        pp.first = std::move(node);
        pp.second = std::move(rn);
        return pp;
      }
    }

    static node_type erase(node_type node, key_type key) {
      if(!node) { return std::move(node); }
      else if(node->key() == key) {
        node_type& left = node->child(0);
        node_type& right = node->child(1);
        if(!left) {
          return node->cut(1);
        }
        else {
          node_type deepest, ln;
          std::tie(deepest, ln) = deepest_node(std::move(left), 1);
          deepest->set(std::move(ln), 0);
          deepest->set(std::move(right), 1);
          return balance(std::move(deepest));
        }
      }
      else if(node->key() < key) {
        auto right = node->cut(1);
        node->set(erase(std::move(right), std::move(key)), 1);
        return balance(std::move(node));
      }
      else {
        auto left = node->cut(0);
        node->set(erase(std::move(left), std::move(key)), 0);
        return balance(std::move(node));
      }
    }

    static node_type& at(node_type& node, key_type key) {
      if(!node) assert(false);
      else if(node->key() == key) return node;
      else if(node->key() < key) return at(node->child(1), std::move(key));
      else return at(node->child(0), std::move(key));
    }

    static node_type& lower_bound(node_type& node, key_type key) {
      if(!node) return node;
      else if(key < node->key()) {
        auto& ans = lower_bound(node->child(0), std::move(key));
        if(ans) return ans;
        else return node;
      }
      else {
        return lower_bound(node->child(1), std::move(key));
      }
    }

    static node_type& nth_node(node_type& node, size_type i) {
      if(size(node->child(0)) == i) return node;
      else if(size(node->child(0)) < i) return nth_node(node->child(1), i - size(node->child(0)) - 1);
      else return nth_node(node->child(0), i);
    }

    static value_type left_fold(node_type& node, key_type left) {
      if(!node) { return identity(); }
      if(node->key() < left) { return left_fold(node->child(1), std::move(left)); }
      else { 
        value_type R = identity();
        if(node->child(1)) R = node->child(1)->fold;
        return operation(left_fold(node->child(0), std::move(left)), operation(node->value(), R));
      }
    }

    static value_type right_fold(node_type& node, key_type right) {
      if(!node) { return identity(); }
      if(!(node->key() < right)) { return right_fold(node->child(0), std::move(right)); }
      else {
        value_type L = identity();
        if(node->child(0)) L = node->child(0)->fold;
        return operation(L, operation(node->value(), right_fold(node->child(1), std::move(right))));
      }
    }

    static value_type go_fold(node_type& node, key_type left, key_type right) {
      if(!node) return identity();
      if(left <= node->key() && node->key() < right) {
        return operation(left_fold(node->child(0), std::move(left)), operation(node->value(), right_fold(node->child(1), std::move(right))));
      }
      else if(node->key() < left) {
        return go_fold(node->child(1), std::move(left), std::move(right));
      }
      else {
        return go_fold(node->child(0), std::move(left), std::move(right));
      }
    }

    node_type root;
    avl_tree_map(node_type&& root): root(std::move(root)) {}
  public:
    avl_tree_map(): root() {}
    avl_tree_map(avl_tree_map&& tree): root(std::move(tree.root)) {}
    avl_tree_map& operator=(avl_tree_map&& tree) {
      root = std::move(tree.root);
      return *this;
    }

    value_type& at(key_type key) {
      return at(root, std::move(key))->value();
    }

    void insert(key_type key, value_type val) {
      root = insert(std::move(root), std::move(key), std::move(val));
    }

    void erase(key_type key) {
      root = erase(std::move(root), std::move(key));
    }

    std::pair<bool, std::pair<key_type, value_type>> lower_bound(key_type key) {
      auto& node = lower_bound(root, std::move(key));
      if(node) {
        return { true, { node->key(), node->value() } };
      }
      else {
        return { false, { key_type(), value_type() } };
      }
    }

    std::pair<key_type, value_type> nth_node(size_type i) {
      auto node = nth_node(root, i);
      return { node->key(), node->value() };
    }

    size_type size() {
      if(!root) return 0;
      return root->size();
    }

    value_type fold(key_type left, key_type right) {
      return go_fold(root, left, right);
    }
};

Persistent AVL Tree Array

列を永続的に管理するAVL Tree

Spec

  • struct node

    • Persistent AVL Treeに載せるノードの型

    • node(value_ref rootval, node_ref a, node_ref b)

      • 永続化ではノードを作るときだけfixする
      • ここにfix操作を書く.
  • split(size_type i)

    • [0, i) / [i, ..)に分けた木を返す
  • merge(persistent_avl_array&& arr)

    • mergeした木を返す.
  • at(size_type i)

    • i番目の要素にアクセスする.
  • push_back(val)

    • 列の一番うしろに要素を追加する.

Code

#include <bits/stdc++.h>
#include <cassert>
using i64 = long long;

class persistent_avl_array {
  using T = char;

  struct node;

  using node_type = typename std::shared_ptr<const node>;
  using node_ref = const node_type&;
  using value_type = T;
  using value_ref = const value_type&;
  using size_type = i64;
  using split_node_type = std::pair<node_type, node_type>;

  static size_type size(node_ref n) {
    if(n) return n->sz;
    else return 0;
  }

  static size_type height(node_ref n) {
    if(n) return n->he;
    else return 0;
  }

  struct node {
    
    using node_type = std::shared_ptr<const node>;
    using node_ref = const node_type&;
    using value_type = T;
    using value_ref = const value_type&;
    using size_type = i64;

    value_type val;
    node_type ch[2];
    size_type sz;
    size_type he;

    node(value_ref val, node_ref left, node_ref right)
      : val(val) {
        sz = 1 + size(left) + size(right);
        he = 1 + std::max(height(left), height(right));
        assert(std::abs(height(left)-height(right))<=1);
        ch[0] = left;
        ch[1] = right;
      }
  };

  static node_type balance(value_ref rootval, node_ref a, node_ref b) {
    if(height(a) - height(b) == 2) {
      if(height(a->ch[0]) - height(a->ch[1]) == -1)
        return std::make_shared<const node>(
            a->ch[1]->val,
            std::make_shared<const node> (a->val, a->ch[0], a->ch[1]->ch[0]),
            std::make_shared<const node> (rootval, a->ch[1]->ch[1], b)
            );
      else
        return std::make_shared<const node>(
            a->val, 
            a->ch[0],
            std::make_shared<const node> (rootval, a->ch[1], b)
            );
    }
    else if(height(a) - height(b) == -2) {
      if(height(b->ch[0]) - height(b->ch[1]) == 1)
        return std::make_shared<const node> (
            b->ch[0]->val,
            std::make_shared<const node> (rootval, a, b->ch[0]->ch[0]),
            std::make_shared<const node> (b->val, b->ch[0]->ch[1], b->ch[1])
            );
      else
        return std::make_shared<const node> (
            b->val,
            std::make_shared<const node>(rootval, a, b->ch[0]),
            b->ch[1]
            );
    }
    else
      return std::make_shared<const node>(rootval, a, b);
  }

  static node_ref back(node_ref node) {
    if(node->ch[1]) return back(node->ch[1]);
    else return node;
  }

  static node_type push_back(node_ref node, value_ref val) {
    if(!node) return std::make_shared<const struct node>(val, nullptr, nullptr);
    else if(node->ch[1]) return merge(node->val, node->ch[0], push_back(node->ch[1], val));
    else return merge(node->val, node->ch[0], std::make_shared<const struct node>(val, nullptr, nullptr));
  }

  static node_type pop_back(node_ref node) {
    if(node->ch[1]) return merge(node->val, node->ch[0], pop_back(node->ch[1]));
    else return node->ch[0];
  }

  static node_type merge_1(value_ref rootval, node_ref dst, node_ref src) {
    if(height(dst) - height(src) <= 1)
      return std::make_shared<const node>(rootval, dst, src);
    else
      return balance(dst->val, dst->ch[0], merge_1(rootval, dst->ch[1], src));
  }

  static node_type merge_0(value_ref rootval, node_ref dst, node_ref src) {
    if(height(dst) - height(src) <= 1)
      return std::make_shared<const node>(rootval, src, dst);
    else
      return balance(dst->val, merge_0(rootval, dst->ch[0], src), dst->ch[1]);
  }

  static node_type merge(value_ref rootval, node_ref left, node_ref right) {
    if(height(left) >= height(right)) 
      return merge_1(rootval, left, right);
    else
      return merge_0(rootval, right, left);
  }

  static split_node_type split(node_ref node, size_type i) {
    if(i == 0)
      return split_node_type(node_type(), node);
    else if(i <= size(node->ch[0])) {
      auto sp = split(node->ch[0], i);
      return split_node_type(sp.first, merge(node->val, sp.second, node->ch[1]));
    }
    else {
      auto sp = split(node->ch[1], i - size(node->ch[0]) - 1);
      return split_node_type(merge(node->val, node->ch[0], sp.first), sp.second);
    }
  }

  static node_ref at(node_ref node, size_type i) {
    if(i == size(node->ch[0])) return node;
    else if(i < size(node->ch[0])) return at(node->ch[0], i);
    else return at(node->ch[1], i - 1 - size(node->ch[0]));
  }

  node_type root;

public:
  
  using split_array_type = std::pair<persistent_avl_array, persistent_avl_array>;

  persistent_avl_array() {}
  persistent_avl_array(node_ref r) : root(r) {}

  persistent_avl_array merge(persistent_avl_array other) const {
    if(!root) return other;
    else return persistent_avl_array(merge(back(root)->val, pop_back(root), other.root));
  }

  split_array_type split(size_type i) const {
    auto p = split(root, i);
    return { persistent_avl_array(p.first), persistent_avl_array(p.second) };
  }

  persistent_avl_array push_back(value_ref val) const {
    return persistent_avl_array(push_back(root, val));
  }

  size_type len() const { return size(root); }

  value_ref at(size_type i) const { return at(root, i)->val; }
};

using namespace std;

void debug_cout(const persistent_avl_array& arr) {
    for(int i = 0;i < arr.len();i++){
      cout << arr.at(i);
    }
    cout << endl;
}

int main() {
  std::ios::sync_with_stdio(false);
  cin.tie(nullptr);
  i64 m;
  cin >> m;
  string s;
  cin >> s;
  i64 n;
  cin >> n;

  persistent_avl_array arr;

  for(char c: s) { arr = arr.push_back(c);}

  for(int i = 0;i < n;i++) {
    i64 a, b, c;
    cin >> a >> b >> c;
    
    auto cpy = arr.split(b).first.split(a).second;
    auto spl = arr.split(c);
    

    arr = spl.first.merge(cpy).merge(spl.second);
    if(arr.len() > m) arr = arr.split(m).first;
  }

  for(int i = 0;i < arr.len();i++){
    cout << arr.at(i);
  }
  cout << endl;
}

Sparse Table

Spec

  • ope

    • 束の演算を定義する.
  • (constructor)

    • Sparse Tableを構築する. \( O(N \log N) \)
  • query(i64 s, i64 t)

    • [s, t)に対してクエリを処理する.

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct sparse_table {
  using Band = int;
  inline Band ope(const Band& a, const Band b) { return min(a, b); }

  i64 N;
  vector<vector<Band>> table;

  sparse_table(vector<Band> arr) : N(arr.size()) {
    table.resize(__lg(N) + 1);

    table[0].resize(N);
    for(int i = 0;i < N;i++) {
      table[0][i] = arr[i];
    }

    for(int k = 1;(1 << k) <= N;k++) {
      table[k].resize(N);
      for(int i = 0;i + (1 << k) <= N;i++) {
        table[k][i] = ope(table[k - 1][i], table[k - 1][i + (1 << (k - 1))]);
      }
    }
  }
  /* [s, t) */
  Band query(i64 s, i64 t) {
    int k = __lg(t - s);
    return ope(table[k][s], table[k][t - (1 << k)]);
  }
};

Sparse Table

Spec

  • ope

    • 束の演算を定義する.
  • (constructor)

    • Sparse Tableを構築する. \( O(N \log N) \)
  • query(i64 s, i64 t)

    • [s, t)に対してクエリを処理する.

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct sparse_table {
  using Band = int;
  inline Band ope(const Band& a, const Band b) { return min(a, b); }

  i64 N;
  vector<vector<Band>> table;

  sparse_table(vector<Band> arr) : N(arr.size()) {
    table.resize(__lg(N) + 1);

    table[0].resize(N);
    for(int i = 0;i < N;i++) {
      table[0][i] = arr[i];
    }

    for(int k = 1;(1 << k) <= N;k++) {
      table[k].resize(N);
      for(int i = 0;i + (1 << k) <= N;i++) {
        table[k][i] = ope(table[k - 1][i], table[k - 1][i + (1 << (k - 1))]);
      }
    }
  }
  /* [s, t) */
  Band query(i64 s, i64 t) {
    int k = __lg(t - s);
    return ope(table[k][s], table[k][t - (1 << k)]);
  }
};

Wavelet Matrix

Wavelet Matrix

Spec

Code

#include <cstdint>
#include <set>
#include <vector>
#include <iostream>

using i64 = long long;

class bitvector {
  using bit_type = std::uint_least64_t;
  using size_type = std::size_t;
  static constexpr size_type wordsize = 64;
  
  std::vector<bit_type> bit;
  std::vector<size_type> sum;
  
public:

  bitvector() : bit(), sum() {} 
  bitvector(const size_type size)
    : bit(size / wordsize + 1, 0), sum(size / wordsize + 1, 0) {}

  void set(const size_type i) {
    bit[i / wordsize] |= static_cast<bit_type>(1) << (i % wordsize);
  }
  void build() {
    for (size_type i = 1; i < bit.size(); i++) {
      sum[i] = sum[i - 1] + __builtin_popcountll(bit[i - 1]);
    }
  }

  size_type at(const size_type i) const {
    return bit[i / wordsize] >> (i % wordsize);
  }

  // count of ones in [0, i)
  size_type rank(const size_type i) const {
    return sum[i / wordsize]
      + __builtin_popcountll(bit[i / wordsize] & (static_cast<bit_type>(1) << (i % wordsize)) - 1);
  }

  // count of ones in [0, i)
  size_type rank(const size_type i, const size_type b) const {
    size_type ans = sum[i / wordsize]
      + __builtin_popcountll(bit[i / wordsize] & (static_cast<bit_type>(1) << (i % wordsize)) - 1);
    if(b) return ans;
    else return i - ans;
  }
};

class wavelet_matrix {
  using Integer = i64;
  using integer_type = Integer;
  using size_type = std::size_t;


  const size_type depth;
  const size_type len;
  std::vector<bitvector> mat;
  std::vector<size_type> spl;

public:

  wavelet_matrix(const std::vector<integer_type>& arr, size_type de)
    : depth(de),
      len(arr.size()),
      mat(std::vector<bitvector>(depth, bitvector(arr.size()))),
      spl(std::vector<size_type>(depth, 0)) {
        std::vector<size_type> idx(len);
        std::vector<size_type> left(len), right(len);
        for(size_type i = 0;i < len;i++) idx[i] = i;
        for(size_type d = depth; d-- > 0;) {
          size_type l = 0, r = 0;
          
          for(size_type i = 0; i < len; i++) {
            size_type k = (arr[idx[i]] >> d) & 1;
            if(k) right[r++] = idx[i], mat[d].set(i);
            else left[l++] = idx[i];
          }
          mat[d].build();
          spl[d] = l;
          swap(idx, left);
          for(size_type i = 0; i < r; i++) idx[i + l] = right[i];
        }
      }

  integer_type at(size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type k = mat[d].at(i);
      x |= (static_cast<integer_type>(k) << d);
      i = mat[d].rank(i, k) + spl[d] * k;
    }
    return x;
  }

  // counting elements that equal to x in range [left, right)
  size_type rank_x(size_type left, size_type right, integer_type x) const {
    for(size_type d = depth; d-- > 0;) {
      size_type k = ((x >> d) & 1);
      left = mat[d].rank(left, k) + spl[d] * k;
      right = mat[d].rank(right, k) + spl[d] * k;
    }
    return right - left;
  }

  // sorted(arr[left..right])[i]
  integer_type quantile(size_type left, size_type right, size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type cnt = mat[d].rank(right, 0) - mat[d].rank(left, 0);
      size_type k = (i < cnt) ? 0 : 1;
      if(k == 1) {
        i -= cnt;
        x |= (1 << d);
      }
      left = mat[d].rank(left, k) + spl[d] * k;
      right = mat[d].rank(right, k) + spl[d] * k;
    }
    return x;
  }

  struct rank_result {
    size_type le;
    size_type eq;
    size_type mo;
  };

  // couting elements that less than x, equal to x, and more than x in range [left, right)
  rank_result rank_less_eq_more(size_type left, size_type right, integer_type x) const {
    size_type le = 0, mo = 0;
    for(size_type d = depth; d --> 0;) {
      size_type k = (x >> d) & 1;
      size_type l = mat[d].rank(left, 1);
      size_type r = mat[d].rank(right, 1);
      if(k == 0) {
        mo += r - l;
        left -= l;
        right -= r;
      }
      else {
        le += (right - left) - (r - l);
        left = l + spl[d];
        right = r + spl[d];
      }
    }
    return rank_result { le, right - left, mo };
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y, integer_type l, size_type d) const {
    integer_type r = l + (1 << d);
    if(x <= l && r <= y) {
      return right - left;
    }
    else if(y <= l || r <= x) {
      return 0;
    }
    else {
      d--;
      size_type lr = mat[d].rank(left, 1);
      size_type rr = mat[d].rank(right, 1);
      return
        rangefreq(left - lr, right - rr, x, y, l, d) +
        rangefreq(lr + spl[d], rr + spl[d], x, y, l + (1 << d), d);
    }
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y) const {
    return rangefreq(left, right, x, y, 0, depth);
  }
};

Dynamic Wavelet Matrix

Spec

Code

#include <array>
#include <set>
#include <tuple>
#include <vector>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <bitset>

struct dynamic_bitvector {


  struct node;
  using size_type = std::size_t;
  using height_type = long long;
  using node_type = std::unique_ptr<struct node>;
  using node_reference = const node_type&;
  using bits_type = std::uint64_t;
  using children_type = std::array<node_type, 2>;


  const static size_type bit_limit = 32;

  struct bitvector_builder {
    const static size_type bit_size = bit_limit * 2;
    const size_type len;
    std::vector<bits_type> bits;
    bitvector_builder(size_type len): len(len), bits(len / bit_size + !!(len & (bit_size - 1))) {}
    void set(size_type i) { bits[i / bit_size] |= (bits_type(1) << (i & (bit_size - 1))); }
    dynamic_bitvector build() const {
      return dynamic_bitvector(bits, len);
    }
  };

  static size_type bits_popcount(bits_type bits) {
    return __builtin_popcountll(bits);
  }

  struct section_t {
    height_type he;
    children_type ch;
  };

  struct leaf_t {
    bits_type bits;
  };

  union info_t {
    section_t section;
    leaf_t leaf;
    info_t(section_t sec): section(std::move(sec)) {}
    info_t(leaf_t leaf): leaf(leaf) {}
    ~info_t() {}
  };

  struct node {
    const bool is_leaf;
    size_type sz;
    size_type popcnt;
    info_t info;

    static node_type new_section(node_type left, node_type right) {
      node* n = new node(section_t { 0, children_type { std::move(left), std::move(right) } });
      n->fix();
      return node_type(n);
    }

    static node_type new_leaf(bits_type bits, size_type sz, size_type popcnt) {
      node* n = new node(leaf_t { bits });
      n->sz = sz;
      n->popcnt = popcnt;
      return node_type(n); } node(section_t sec): is_leaf(false), info(std::move(sec)) {}
    node(leaf_t leaf): is_leaf(true), info(leaf) {}

    height_type height() const {
      if(is_leaf) return 0;
      else return info.section.he;
    }
    size_type size() const {
      return sz;
    }

    size_type popcount() const {
      return popcnt;
    }

    // operation for only section node

    node_type take(size_type dir) {
      assert(!is_leaf);
      return std::move(info.section.ch[dir]);
    }

    node_type swap(size_type dir, node_type new_node) {
      assert(!is_leaf);
      node_type old_node = take(dir);
      info.section.ch[dir] = std::move(new_node);
      return old_node;
    }

    const node_type& child(size_type dir) const {
      assert(!is_leaf);
      return info.section.ch[dir];
    }

    height_type diff() const {
      assert(!is_leaf);
      return child(0)->height() - child(1)->height();
    }

    void fix() {
      assert(!is_leaf);
      sz = child(0)->size() + child(1)->size();
      popcnt = child(0)->popcount() + child(1)->popcount();
      info.section.he = std::max(child(0)->height(), child(1)->height()) + 1;
    }

    // operation for only leaf node

    bits_type bits() const {
      assert(is_leaf);
      return info.leaf.bits;
    }

    bool at_bits(size_type pos) const {
      assert(is_leaf);
      return (bits() >> pos) & 1;
    }

    size_type rank(size_type pos) const {
      assert(pos <= size());
      return bits_popcount(bits() & ((bits_type(1) << pos) - 1));
    }

    void set_bits(bits_type bits, size_type sz_, size_type popcnt_) {
      assert(is_leaf);
      sz = sz_;
      popcnt = popcnt_;
      info.leaf.bits = bits;
    }

    // [0, pos) and [pos, sz)
    std::pair<bits_type, bits_type> split_bits(size_type pos) const {
      assert(is_leaf);
      assert(pos <= size());
      return std::make_pair(bits() & ((bits_type(1) << pos) - 1), bits() >> pos);
    }

    void insert_bit(size_type pos, bool bit) {
      assert(is_leaf);
      assert(sz < bit_limit * 2);
      bits_type l, r;
      std::tie(l, r) = split_bits(pos);
     // std::cout << "insert " << bit  << " to " << pos << std::endl;
     // std::cout << "insert " << std::bitset<bit_limit * 2>(l) << " " << std::bitset<bit_limit * 2>(r) << std::endl;
     // std::cout << "insert " << std::bitset<bit_limit * 2>(bits()) << " -> " << std::bitset<bit_limit * 2>(l | ((bits_type(bit) << pos)) | (r << (pos + 1))) << std::endl;
      set_bits(l | ((bits_type(bit) << pos)) | (r << (pos + 1)), sz + 1, popcount() + bit);
    } 

    void erase_bit(size_type pos) {
      assert(is_leaf);
      assert(0 < sz);
      bits_type l, r;
      std::tie(l, r) = split_bits(pos);
      size_type pos_bit = r & 1;
      r >>= 1;
      set_bits(l | (r << pos), sz - 1, popcount() - pos_bit);
    }
  };


  static node_type rotate(node_type x, size_type dir) {
    x->fix();
    node_type y = x->take(1 - dir);
    node_type b = y->take(dir);
    x->swap(1 - dir, std::move(b));
    x->fix();
    y->swap(dir, std::move(x));
    y->fix();
    return std::move(y);
  }

  static node_type balance(node_type node) {
    node->fix();
    if(node->diff() == 2) {
      if(node->child(0)->diff() == -1) {
        auto ch = node->take(0);
        node->swap(0, rotate(std::move(ch), 0));
      }
      return rotate(std::move(node), 1);
    }
    else if(node->diff() == -2) {
      if(node->child(1)->diff() == 1) {
        auto ch = node->take(1);
        node->swap(1, rotate(std::move(ch), 1));
      }
      return rotate(std::move(node), 0);
    }
    else return std::move(node);
  }

  static node_type split_node(node_type node) {
    assert(node->is_leaf);
    bits_type l, r;
    std::tie(l, r) = node->split_bits(bit_limit);
    return node::new_section(
        node::new_leaf(l, bit_limit, bits_popcount(l)), node::new_leaf(r, node->size() - bit_limit, bits_popcount(r))
        );
  }

  static node_type insert(node_type node, size_type pos, bool bit) {
    if(node->is_leaf) {
      if(node->size() == 2 * bit_limit) {
        return insert(split_node(std::move(node)), pos, bit);
      }
      else {
        node->insert_bit(pos, bit);
        return node;
      }
    }
    else if(pos < node->child(0)->size()) {
      node->swap(0, insert(node->take(0), pos, bit));
      return balance(std::move(node));
    }
    else {
      node->swap(1, insert(node->take(1), pos - node->child(0)->size(), bit));
      return balance(std::move(node));
    }
  }

  template<const size_type dir>
  static std::tuple<node_type, bits_type, size_type> take_bit(node_type node, size_type len) {
    if(node->is_leaf) {
      if(node->size() < bit_limit / 2 + len) {
        return std::tuple<node_type, bits_type, size_type>{ node_type(nullptr), node->bits(), node->size() };
      }
      else {
        auto p = node->split_bits(dir == 0 ? len : node->size() - len);
        bits_type node_bit = std::get<1 - dir>(p);
        bits_type take = std::get<dir>(p);
        node->set_bits(node_bit, node->size() - len, bits_popcount(node_bit));
        return std::tuple<node_type, bits_type, size_type> { std::move(node), take, len };
      }
    }
    else {
      node_type ch;
      bits_type bits;
      size_type take_len;
      std::tie(ch, bits, take_len) = take_bit<dir>(node->take(dir), len);
      if(ch) {
        node->swap(dir, std::move(ch));
        return std::tuple<node_type, bits_type, size_type>{ balance(std::move(node)), bits, take_len };
      }
      else {
        return std::tuple<node_type, bits_type, size_type> { node->take(1 - dir), bits, take_len };
      }
    }
  }

  static node_type erase(node_type node, size_type pos) {
    if(node->is_leaf) {
      node->erase_bit(pos);
      return node;
    }
    else if(pos < node->child(0)->size()) {
      auto left = erase(node->take(0), pos);
      if(left->size() < bit_limit / 2) {
        assert(left->is_leaf);
        node_type right;
        bits_type bits;
        size_type len;
        std::tie(right, bits, len) = take_bit<0>(node->take(1), bit_limit / 2 - left->size());

        left->set_bits(left->bits() | (bits << left->size()), left->size() + len, left->popcount() + bits_popcount(bits));
        if(right) {
          node->swap(0, std::move(left));
          node->swap(1, std::move(right));
          return balance(std::move(node));
        }
        else {
          return left;
        }
      }
      else {
        node->swap(0, std::move(left));
        return balance(std::move(node));
      }
    }
    else {
      auto right = erase(node->take(1), pos - node->child(0)->size());
      if(right->size() < bit_limit / 2) {
        assert(right->is_leaf);
        node_type left;
        bits_type bits;
        size_type len;
        std::tie(left, bits, len) = take_bit<1>(node->take(0), bit_limit / 2 - right->size());

        right->set_bits((right->bits() << len) | bits, right->size() + len, right->popcount() + bits_popcount(bits));
        if(left) {
          node->swap(0, std::move(left));
          node->swap(1, std::move(right));
          return balance(std::move(node));
        }
        else {
          return right;
        }
      }
      else {
        node->swap(1, std::move(right));
        return balance(std::move(node));
      }
    }
  }

  static node_type merge_dir(node_type dst, node_type src, size_type dir) {
    if(std::abs(dst->height() - src->height()) <= 1) {
      if(dir == 0)
        return node::new_section(std::move(src), std::move(dst));
      else
        return node::new_section(std::move(dst), std::move(src));
    }
    else {
      node_type ch = dst->take(dir);
      assert(ch);
      dst->swap(dir, merge_dir(std::move(ch), std::move(src), dir));
      return balance(std::move(dst));
    }
  }

  static node_type merge(node_type left, node_type right) {
    if(!left) { return std::move(right); }
    else if(!right) { return std::move(left); }
    else if(left->height() >= right->height()) {
      return merge_dir(std::move(left), std::move(right), 1);
    }
    else {
      return merge_dir(std::move(right), std::move(left), 0);
    }
  }

  static node_type build(const std::vector<bits_type>& bits, size_type l, size_type r, size_type len) {
    if(len == 0) node::new_leaf(0, 0, 0);
    //std::cout << "build " << l << " " << r << " " << len << std::endl;
    if(l + 1 >= r) {
      //std::cout << "create leaf" << std::endl;
      //std::cout << "-----------" << std::endl;
      return node::new_leaf(bits[l], len, bits_popcount(bits[l]));
    }
    else {
      size_type m = (l + r) >> 1;
      return merge(build(bits, l, m, (m - l) * 64), build(bits, m, r, len - (m - l) * 64));
    }
  }

  static size_type at(node_reference node, size_type pos) {
    if(node->is_leaf) {
      return node->at_bits(pos);
    }
    else if(pos < node->child(0)->size()) {
      return at(node->child(0), pos);
    }
    else {
      return at(node->child(1), pos - node->child(0)->size());
    }
  }

  static size_type rank(node_reference node, size_type pos) {
    if(node->is_leaf) {
      return node->rank(pos);
    }
    else if(pos == node->size()) {
      return node->popcount();
    }
    else if(pos < node->child(0)->size()) {
      return rank(node->child(0), pos);
    }
    else {
      return node->child(0)->popcount() + rank(node->child(1), pos - node->child(0)->size());
    }
  }


  node_type root;

  dynamic_bitvector(): root(node::new_leaf(0, 0, 0)) {}
  dynamic_bitvector(const std::vector<bits_type>& bits, size_type len): root(build(bits, 0, bits.size(), len)) {}
  void insert(size_type pos, bool bit) {
    root = insert(std::move(root), pos, bit);
  }
  void erase(size_type pos) {
    root = erase(std::move(root), pos);
  }
  bool at(size_type pos) const {
    return at(root, pos);
  }
  size_type rank(size_type pos) const {
    return rank(root, pos);
  }
  size_type rank(size_type pos, bool bit) const {
    if(bit) {
      return rank(root, pos);
    }
    else {
      return pos - rank(root, pos);
    }
  }
  size_type size() const {
    return root->size();
  }

  void debug_tree(node_reference node, std::string d) const {
    if(node->is_leaf) {
      std::cout << d << "leaf " << node->size() << " " << node->popcount() << " -----------" << std::endl;
      std::cout << d << std::bitset<bit_limit * 2>(node->bits()) << std::endl;
      std::cout << d << "-------------" << std::endl;
    }
    else {
      std::cout << d << "node " << node->size() << " " << node->popcount() << " ----------" << std::endl;
      std::cout << d << "left" << std::endl;
      debug_tree(node->child(0), d + "   ");
      std::cout << d << "--------------" << std::endl;
      std::cout << d << "right" << std::endl;
      debug_tree(node->child(1), d + "   ");
      std::cout << d << "--------------" << std::endl;
    }
  }

  void debug_tree() const {
    debug_tree(root, "");
  }
};

#include <vector>
struct dynamic_wavelet_matrix {
  using Integer = long long;
  using integer_type = Integer;
  using size_type = std::size_t;


  const size_type depth;
  const size_type len;
  std::vector<dynamic_bitvector> mat;
  std::vector<size_type> spl;

public:

  dynamic_wavelet_matrix(const std::vector<integer_type>& arr, size_type de)
    : depth(de),
      mat(de),
      len(arr.size()),
      spl(std::vector<size_type>(depth, 0)) {
        std::vector<size_type> idx(len);
        std::vector<size_type> left(len), right(len);

        for(size_type i = 0;i < len;i++) idx[i] = i;

        for(size_type d = depth; d-- > 0;) {
          size_type l = 0, r = 0;
          dynamic_bitvector::bitvector_builder builder(len);
          for(size_type i = 0; i < len; i++) {
            size_type k = (arr[idx[i]] >> d) & 1;
            if(k) right[r++] = idx[i], builder.set(i);
            else left[l++] = idx[i];
          }
          mat[d] = builder.build();
          spl[d] = l;
          swap(idx, left);
          for(size_type i = 0; i < r; i++) idx[i + l] = right[i];
        }
      }

  void insert(size_type i, integer_type x) {
    for(size_type d = depth; d --> 0;) {
      size_type k = ((x >> d) & 1);
      mat[d].insert(i, k);
      i = mat[d].rank(i, k) + spl[d] * k;
      if(k == 0) spl[d]++;
    }
  }

  void erase(size_type i) {
    for(size_type d = depth; d--> 0;) {
      size_type k = mat[d].at(i);
      mat[d].erase(i);
      i = mat[d].rank(i, k) + spl[d] * k;
      if(k == 0) spl[d]--;
    }
  }

  integer_type at(size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type k = mat[d].at(i);
      x |= (static_cast<integer_type>(k) << d);
      i = mat[d].rank(i, k) + spl[d] * k;
    }
    return x;
  }

  // counting elements that equal to x in range [left, right)
  size_type rank_x(size_type left, size_type right, integer_type x) const {
    for(size_type d = depth; d-- > 0;) {
      size_type k = ((x >> d) & 1);
      left = mat[d].rank(left, k) + spl[d] * k;
      right = mat[d].rank(right, k) + spl[d] * k;
    }
    return right - left;
  }

  // sorted(arr[left..right])[i]
  integer_type quantile(size_type left, size_type right, size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type l = mat[d].rank(left, 1);
      size_type r = mat[d].rank(right, 1);
      size_type cnt = (right - left) - (r - l);
      size_type k = (i < cnt) ? 0 : 1;
      if(k == 0) {
        left -= l;
        right -= r;
      }
      else {
        x |= (1 << d);
        i -= cnt;
        left = l + spl[d];
        right = r + spl[d];
      }
    }
    return x;
  }

  struct rank_result {
    size_type le;
    size_type eq;
    size_type mo;
  };

  // couting elements that less than x, equal to x, and more than x in range [left, right)
  rank_result rank_less_eq_more(size_type left, size_type right, integer_type x) const {
    size_type le = 0, mo = 0;
    for(size_type d = depth; d --> 0;) {
      size_type k = (x >> d) & 1;
      size_type l = mat[d].rank(left, 1);
      size_type r = mat[d].rank(right, 1);
      if(k == 0) {
        mo += r - l;
        left -= l;
        right -= r;
      }
      else {
        le += (right - left) - (r - l);
        left = l + spl[d];
        right = r + spl[d];
      }
    }
    return rank_result { le, right - left, mo };
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y, integer_type l, size_type d) const {
    integer_type r = l + (1 << d);
    if(x <= l && r <= y) {
      return right - left;
    }
    else if(y <= l || r <= x) {
      return 0;
    }
    else {
      d--;
      size_type lr = mat[d].rank(left, 1);
      size_type rr = mat[d].rank(right, 1);
      return
        rangefreq(left - lr, right - rr, x, y, l, d) +
        rangefreq(lr + spl[d], rr + spl[d], x, y, l + (1 << d), d);
    }
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y) const {
    return rangefreq(left, right, x, y, 0, depth);
  }

  size_type rangefreq_count(size_type left, size_type right, integer_type x, integer_type y) const {
    auto p = rank_less_eq_more(left, right, x);
    auto q = rank_less_eq_more(left, right, y);
    return p.eq + p.mo - q.eq - q.mo;
  }
};

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}


int main() {
  cin.tie(nullptr);
  std::ios::sync_with_stdio(false);

  i64 N, Q;
  cin >> N >> Q;
  vector<i64> A(N);
  rep(i,0,N) cin >> A[i];
  
  dynamic_wavelet_matrix wm(A, 30);

  while(Q--) {
    i64 l, r, k;
    cin >> l >> r >> k;
    cout << wm.quantile(l, r, k) << "\n";
  }
}

Fully Retroactive Stack

Code

#include <cstdint>
#include <algorithm>
#include <iostream>
#include <cassert>

struct retroactive_stack {
  using value_type = long long;
  using size_type = std::size_t;
  using height_type = std::int_least32_t;
  using node_index = std::int_least32_t;

  struct diff {
    value_type p_value;
    height_type d;
    height_type max_d;
    height_type min_d;

    static diff push_diff(value_type value) {
      diff i;
      i.p_value = value;
      i.d = 1;
      i.max_d = 1;
      i.min_d = 0;
      return i;
    }
    static diff pop_diff() {
      diff i;
      i.d = -1;
      i.max_d = 0;
      i.min_d = -1;
      return i;
    }

    static diff ope(const diff& l, const diff& r) {
      diff i;
      i.d = l.d + r.d;
      i.max_d = std::max(l.max_d, l.d + r.max_d);
      i.min_d = std::min(l.min_d, l.d + r.min_d);
      return i;
    }
  };

  struct node;

  static struct node n[2020202];
  static size_type ni;

  struct node {
    diff d;
    size_type s;
    height_type h;
    node_index c[2];
    node_index p;
    node() {}
    node& operator[](size_type d) { return n[c[d]]; }
  };

  static node_index new_node(const diff& d) {
    node_index i = ni++;
    n[i].s = 1;
    n[i].h = 1;
    n[i].d = d;
    return i;
  }
  static void fix(node_index i) {
    n[i].s = n[i][0].s + n[i][1].s;
    n[i].h = std::max(n[i][0].h, n[i][1].h) + 1;
    n[i].d = diff::ope(n[i][0].d, n[i][1].d);
  }

  static size_type child_dir(node_index i) {
    if(n[i].p) {
      if(n[n[i].p].c[0] == i) { return 0; }
      else if(n[n[i].p].c[1] == i) { return 1; }
      assert(false);
    }
    return -1;
  }
  static node_index rotate(node_index x, size_type dir) {
    node_index p = n[x].p;
    size_type x_dir = child_dir(x);
    node_index y = n[x].c[1 ^ dir];
    n[n[y][dir].p = x].c[1 ^ dir] = n[y].c[dir];
    n[n[x].p = y].c[dir] = x;
    n[y].p = p;
    if(p > 0) n[p].c[x_dir] = y;
    fix(x);
    fix(y);
    return y;
  }

  static node_index balance(node_index i) {
    fix(i);
    if(n[i][0].h - n[i][1].h == 2) {
      if(n[i][0][0].h - n[i][0][1].h == -1) {
        rotate(n[i].c[0], 0);
      }
      return rotate(i, 1);
    }
    else if(n[i][0].h - n[i][1].h == -2) {
      if(n[i][1][0].h - n[i][1][1].h == 1) {
        rotate(n[i].c[1], 1);
      }
      return rotate(i, 0);
    }
    return i;
  }


  static node_index merge_dir(node_index l, node_index root, node_index r, size_type dir) {
    while(std::abs(n[l].h - n[r].h) > 1) {
      l = n[l].c[dir];
    }
    node_index x = n[l].p;
    n[n[l].p = root].c[dir ^ 1] = l;
    n[n[r].p = root].c[dir] = r;
    fix(root);
    n[n[root].p = x].c[dir] = root;
    x = root;
    while(n[x].p > 0) {
      x = n[x].p;
      x = balance(x);
    }
    return x;
  }

  static node_index merge(node_index l, node_index r) {
    if(!l) return r;
    else if(!r) return l;
    else if(n[l].h >= n[r].h) {
      return merge_dir(l, new_node(diff()), r, 1);
    }
    else {
      return merge_dir(r, new_node(diff()), l, 0);
    }
  }

  static std::pair<node_index, node_index> split(node_index root, size_type pos) {
    if(pos == 0) return { 0, root };
    if(pos == n[root].s) return { root, 0 };
    node_index i = root;
    node_index par = -1;
    while(i > 0 && pos != n[i][0].s) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    node_index l = n[i].c[0];
    n[l].p = 0;
    node_index r = n[i].c[1];
    n[r].p = 0;
    size_type dir;
    node_index p = n[i].p;
    node_index pd = child_dir(i);
    while(dir = pd, i = p, i > 0) {
      //std::cout << i << " " << dir << std::endl;
      pd = child_dir(i);
      p = n[i].p;
      n[i].p = 0;
      if(dir == 0) {
        n[i][1].p = 0;
        //std::cout << "merge_dir, 0" << std::endl;
        //debug(n[i].c[1], "");
        //debug(r, "");
        r = merge_dir(n[i].c[1], i, r, 0);
        //debug(r, "");
      }
      else {
        n[i][0].p = 0;
        //std::cout << "merge_dir, 0" << std::endl;
        //debug(n[i].c[0], "");
        //debug(l, "");
        l = merge_dir(n[i].c[0], i, l, 1);
        //debug(l, "");
      }
    }
    return { l, r };
  }

  static node_index at(node_index i, size_type pos) {
    while(n[i].c[0]) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    return i;
  }

  static void set(node_index i, size_type pos, diff v) {
    node_index par = -1;
    while(n[i].c[0]) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    n[i].d = v;

    while(i = n[i].p, i > 0) {
      fix(i);
    }
  }

  static size_type index(node_index i) {
    size_type pos = 0;
    while(n[i].p > 0) {
      if(child_dir(i) == 1) {
        pos += n[n[i].p][0].s;
      }
      i = n[i].p;
    }
    return pos;
  }

  static node_index search(node_index i, height_type pos) {
    while(n[i].c[0]) {
      if(n[i][0].d.d + n[i][1].d.min_d <= pos && pos < n[i][0].d.d + n[i][1].d.max_d) {
        pos -= n[i][0].d.d;
        i = n[i].c[1];
      }
      else {
        i = n[i].c[0];
      }
    }
    return i;
  }

  static std::pair<node_index, node_index> min_depth_split(node_index i) {
    node_index r = i;
    size_type pos = n[i].d.min_d;
    size_type res = 0;
    while(n[i].c[0]) {
      if(n[i][0].d.d + n[i][1].d.min_d <= pos && pos < n[i][0].d.d + n[i][1].d.max_d) {
        pos -= n[i][0].d.d;
        res += n[i][0].s;
        i = n[i].c[1];
      }
      else {
        i = n[i].c[0];
      }
    }
    return split(r, res);
  }

  static void debug(node_index i, std::string s) {
    if(i == 0) {
      std::cout << s << 0 << std::endl;
      return;
    }
    std::cout << s << i << " = " << n[i].d.p_value << " = " << n[i].p << " = " << n[i].s << std::endl;
    if(n[i].c[0] && n[i][0].p != i) {
      assert(false);
    }
    debug(n[i].c[0], s + "  ");
    if(n[i].c[1] && n[i][1].p != i) {
      assert(false);
    }
    debug(n[i].c[1], s + "  ");
  }


public:

  node_index root;
  retroactive_stack(): root(0) {}
  retroactive_stack(node_index i): root(i) {}

  static node_index new_push_operation(value_type val) {
    return new_node(diff::push_diff(val));
  }
  static node_index new_pop_operation() {
    return new_node(diff::pop_diff());
  }

  retroactive_stack& merge(retroactive_stack right) {
    root = merge(this->root, right.root);
    return *this;
  }
  retroactive_stack split(node_index i) {
    auto p = split(root, index(i));
    retroactive_stack avl;
    avl.root = p.second;
    root = p.first;
    return avl;
  }

  void update(node_index i, diff v) {
    set(root, index(i), v);
  }

  size_type operation_size() {
    return n[root].s;
  }

  height_type stack_size() {
    return n[root].d.d;
  }

  value_type top() {
    auto P = min_depth_split(root);
    auto res = n[search(P.second, n[P.second].d.d - 1)].d.p_value;
    root = merge(P.first, P.second);
    return res;
  }

  void debug() {
    debug(root, "");
  }
};

retroactive_stack::size_type retroactive_stack::ni = 1;
retroactive_stack::node retroactive_stack::n[2020202];

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}

template<class T, class Cond>
struct chain {
  Cond cond; chain(Cond cond) : cond(cond) {}
  bool operator()(T& a, const T& b) const {
    if(cond(a, b)) { a = b; return true; }
    return false;
  }
};
template<class T, class Cond>
chain<T, Cond> make_chain(Cond cond) { return chain<T, Cond>(cond); }

int main() {
  std::vector<int> opes;
  opes.push_back(retroactive_stack::new_push_operation(1));
  opes.push_back(retroactive_stack::new_push_operation(2));
  opes.push_back(retroactive_stack::new_push_operation(3));

  retroactive_stack st;

  st.merge(retroactive_stack::new_pop_operation());
  st.merge(retroactive_stack::new_pop_operation());
  st.merge(retroactive_stack::new_pop_operation());

  for(auto i: opes) {
    st.merge(retroactive_stack(i));
  }
  std::cout << st.top() << std::endl; // 3

  int pop_ope = retroactive_stack::new_pop_operation();

  st.merge(retroactive_stack(pop_ope));

  std::cout << st.top() << std::endl; // 2

  st.merge(retroactive_stack(retroactive_stack::new_pop_operation()));
  std::cout << st.top() << std::endl; // 1

  st.update(pop_ope, retroactive_stack::diff::push_diff(4));
  std::cout << st.top() << std::endl; // 3 (push 1 -> push 2 -> push 3 -> push 4 -> pop)

  auto after = st.split(opes[2]);
  std::cout << st.top() << std::endl; // 2 st := (push 1 -> push 2)
}

Fully Retroactive Stack

Code

#include <cstdint>
#include <algorithm>
#include <iostream>
#include <cassert>

struct retroactive_stack {
  using value_type = long long;
  using size_type = std::size_t;
  using height_type = std::int_least32_t;
  using node_index = std::int_least32_t;

  struct diff {
    value_type p_value;
    height_type d;
    height_type max_d;
    height_type min_d;

    static diff push_diff(value_type value) {
      diff i;
      i.p_value = value;
      i.d = 1;
      i.max_d = 1;
      i.min_d = 0;
      return i;
    }
    static diff pop_diff() {
      diff i;
      i.d = -1;
      i.max_d = 0;
      i.min_d = -1;
      return i;
    }

    static diff ope(const diff& l, const diff& r) {
      diff i;
      i.d = l.d + r.d;
      i.max_d = std::max(l.max_d, l.d + r.max_d);
      i.min_d = std::min(l.min_d, l.d + r.min_d);
      return i;
    }
  };

  struct node;

  static struct node n[2020202];
  static size_type ni;

  struct node {
    diff d;
    size_type s;
    height_type h;
    node_index c[2];
    node_index p;
    node() {}
    node& operator[](size_type d) { return n[c[d]]; }
  };

  static node_index new_node(const diff& d) {
    node_index i = ni++;
    n[i].s = 1;
    n[i].h = 1;
    n[i].d = d;
    return i;
  }
  static void fix(node_index i) {
    n[i].s = n[i][0].s + n[i][1].s;
    n[i].h = std::max(n[i][0].h, n[i][1].h) + 1;
    n[i].d = diff::ope(n[i][0].d, n[i][1].d);
  }

  static size_type child_dir(node_index i) {
    if(n[i].p) {
      if(n[n[i].p].c[0] == i) { return 0; }
      else if(n[n[i].p].c[1] == i) { return 1; }
      assert(false);
    }
    return -1;
  }
  static node_index rotate(node_index x, size_type dir) {
    node_index p = n[x].p;
    size_type x_dir = child_dir(x);
    node_index y = n[x].c[1 ^ dir];
    n[n[y][dir].p = x].c[1 ^ dir] = n[y].c[dir];
    n[n[x].p = y].c[dir] = x;
    n[y].p = p;
    if(p > 0) n[p].c[x_dir] = y;
    fix(x);
    fix(y);
    return y;
  }

  static node_index balance(node_index i) {
    fix(i);
    if(n[i][0].h - n[i][1].h == 2) {
      if(n[i][0][0].h - n[i][0][1].h == -1) {
        rotate(n[i].c[0], 0);
      }
      return rotate(i, 1);
    }
    else if(n[i][0].h - n[i][1].h == -2) {
      if(n[i][1][0].h - n[i][1][1].h == 1) {
        rotate(n[i].c[1], 1);
      }
      return rotate(i, 0);
    }
    return i;
  }


  static node_index merge_dir(node_index l, node_index root, node_index r, size_type dir) {
    while(std::abs(n[l].h - n[r].h) > 1) {
      l = n[l].c[dir];
    }
    node_index x = n[l].p;
    n[n[l].p = root].c[dir ^ 1] = l;
    n[n[r].p = root].c[dir] = r;
    fix(root);
    n[n[root].p = x].c[dir] = root;
    x = root;
    while(n[x].p > 0) {
      x = n[x].p;
      x = balance(x);
    }
    return x;
  }

  static node_index merge(node_index l, node_index r) {
    if(!l) return r;
    else if(!r) return l;
    else if(n[l].h >= n[r].h) {
      return merge_dir(l, new_node(diff()), r, 1);
    }
    else {
      return merge_dir(r, new_node(diff()), l, 0);
    }
  }

  static std::pair<node_index, node_index> split(node_index root, size_type pos) {
    if(pos == 0) return { 0, root };
    if(pos == n[root].s) return { root, 0 };
    node_index i = root;
    node_index par = -1;
    while(i > 0 && pos != n[i][0].s) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    node_index l = n[i].c[0];
    n[l].p = 0;
    node_index r = n[i].c[1];
    n[r].p = 0;
    size_type dir;
    node_index p = n[i].p;
    node_index pd = child_dir(i);
    while(dir = pd, i = p, i > 0) {
      //std::cout << i << " " << dir << std::endl;
      pd = child_dir(i);
      p = n[i].p;
      n[i].p = 0;
      if(dir == 0) {
        n[i][1].p = 0;
        //std::cout << "merge_dir, 0" << std::endl;
        //debug(n[i].c[1], "");
        //debug(r, "");
        r = merge_dir(n[i].c[1], i, r, 0);
        //debug(r, "");
      }
      else {
        n[i][0].p = 0;
        //std::cout << "merge_dir, 0" << std::endl;
        //debug(n[i].c[0], "");
        //debug(l, "");
        l = merge_dir(n[i].c[0], i, l, 1);
        //debug(l, "");
      }
    }
    return { l, r };
  }

  static node_index at(node_index i, size_type pos) {
    while(n[i].c[0]) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    return i;
  }

  static void set(node_index i, size_type pos, diff v) {
    node_index par = -1;
    while(n[i].c[0]) {
      if(pos < n[i][0].s) {
        i = n[i].c[0];
      }
      else {
        pos -= n[i][0].s;
        i = n[i].c[1];
      }
    }
    n[i].d = v;

    while(i = n[i].p, i > 0) {
      fix(i);
    }
  }

  static size_type index(node_index i) {
    size_type pos = 0;
    while(n[i].p > 0) {
      if(child_dir(i) == 1) {
        pos += n[n[i].p][0].s;
      }
      i = n[i].p;
    }
    return pos;
  }

  static node_index search(node_index i, height_type pos) {
    while(n[i].c[0]) {
      if(n[i][0].d.d + n[i][1].d.min_d <= pos && pos < n[i][0].d.d + n[i][1].d.max_d) {
        pos -= n[i][0].d.d;
        i = n[i].c[1];
      }
      else {
        i = n[i].c[0];
      }
    }
    return i;
  }

  static std::pair<node_index, node_index> min_depth_split(node_index i) {
    node_index r = i;
    size_type pos = n[i].d.min_d;
    size_type res = 0;
    while(n[i].c[0]) {
      if(n[i][0].d.d + n[i][1].d.min_d <= pos && pos < n[i][0].d.d + n[i][1].d.max_d) {
        pos -= n[i][0].d.d;
        res += n[i][0].s;
        i = n[i].c[1];
      }
      else {
        i = n[i].c[0];
      }
    }
    return split(r, res);
  }

  static void debug(node_index i, std::string s) {
    if(i == 0) {
      std::cout << s << 0 << std::endl;
      return;
    }
    std::cout << s << i << " = " << n[i].d.p_value << " = " << n[i].p << " = " << n[i].s << std::endl;
    if(n[i].c[0] && n[i][0].p != i) {
      assert(false);
    }
    debug(n[i].c[0], s + "  ");
    if(n[i].c[1] && n[i][1].p != i) {
      assert(false);
    }
    debug(n[i].c[1], s + "  ");
  }


public:

  node_index root;
  retroactive_stack(): root(0) {}
  retroactive_stack(node_index i): root(i) {}

  static node_index new_push_operation(value_type val) {
    return new_node(diff::push_diff(val));
  }
  static node_index new_pop_operation() {
    return new_node(diff::pop_diff());
  }

  retroactive_stack& merge(retroactive_stack right) {
    root = merge(this->root, right.root);
    return *this;
  }
  retroactive_stack split(node_index i) {
    auto p = split(root, index(i));
    retroactive_stack avl;
    avl.root = p.second;
    root = p.first;
    return avl;
  }

  void update(node_index i, diff v) {
    set(root, index(i), v);
  }

  size_type operation_size() {
    return n[root].s;
  }

  height_type stack_size() {
    return n[root].d.d;
  }

  value_type top() {
    auto P = min_depth_split(root);
    auto res = n[search(P.second, n[P.second].d.d - 1)].d.p_value;
    root = merge(P.first, P.second);
    return res;
  }

  void debug() {
    debug(root, "");
  }
};

retroactive_stack::size_type retroactive_stack::ni = 1;
retroactive_stack::node retroactive_stack::n[2020202];

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}

template<class T, class Cond>
struct chain {
  Cond cond; chain(Cond cond) : cond(cond) {}
  bool operator()(T& a, const T& b) const {
    if(cond(a, b)) { a = b; return true; }
    return false;
  }
};
template<class T, class Cond>
chain<T, Cond> make_chain(Cond cond) { return chain<T, Cond>(cond); }

int main() {
  std::vector<int> opes;
  opes.push_back(retroactive_stack::new_push_operation(1));
  opes.push_back(retroactive_stack::new_push_operation(2));
  opes.push_back(retroactive_stack::new_push_operation(3));

  retroactive_stack st;

  st.merge(retroactive_stack::new_pop_operation());
  st.merge(retroactive_stack::new_pop_operation());
  st.merge(retroactive_stack::new_pop_operation());

  for(auto i: opes) {
    st.merge(retroactive_stack(i));
  }
  std::cout << st.top() << std::endl; // 3

  int pop_ope = retroactive_stack::new_pop_operation();

  st.merge(retroactive_stack(pop_ope));

  std::cout << st.top() << std::endl; // 2

  st.merge(retroactive_stack(retroactive_stack::new_pop_operation()));
  std::cout << st.top() << std::endl; // 1

  st.update(pop_ope, retroactive_stack::diff::push_diff(4));
  std::cout << st.top() << std::endl; // 3 (push 1 -> push 2 -> push 3 -> push 4 -> pop)

  auto after = st.split(opes[2]);
  std::cout << st.top() << std::endl; // 2 st := (push 1 -> push 2)
}

Hash Map

Code

#include <vector>
#include <cstdint>
#include <set>
#include <iostream>

template<class Key, class Value, class Hash>
struct hash_map {
  using size_type = std::size_t;
  using key_type = Key;
  using value_type = Value;
  using pair_type = std::pair<key_type, value_type>;

  enum class State: std::uint8_t {
    INACTIVE,
    ACTIVE,
    FILLED
  };

  Hash hashf;

  std::vector<State> st;
  std::vector<pair_type> bck;
  size_type mask;
  size_type prode;
  size_type sz;
  size_type min_elem;
  hash_map():
    mask(0), prode(-1), sz(0), min_elem(0) {
  }

  size_type find_empty(const key_type& key) {
    size_type h = hashf(key);
    for(size_type delta = 0;;delta++) {
      size_type i = (h + delta) & mask;
      if(st[i] != State::FILLED) {
        if(prode < delta) prode = delta;
        return i;
      }
    }
  }

  size_type find_filled(const key_type& key) {
    if(sz == 0) return size_type(-1);
    size_type h = hashf(key);
    for(size_type delta = 0; delta <= prode; delta++) {
      size_type i = (h + delta) & mask;
      if(st[i] == State::FILLED) {
        if(bck[i].first == key) {
          return i;
        }
      }
      else if(st[i] == State::INACTIVE) {
        return size_type(-1);
      }
    }
    return size_type(-1);
  }

  size_type find_or_allocate(const key_type& key) {
    size_type h = hashf(key);
    size_type hole = size_type(-1);
    size_type delta = 0;
    for(; delta <= prode; delta++) {
      size_type i = (h + delta) & mask;
      if(st[i] == State::FILLED) {
        if(bck[i].first == key) return i;
      }
      else if(st[i] == State::INACTIVE) return i;
      else {
        if(hole == size_type(-1)) {
          hole = i;
        }
      }
    }
    if(hole != size_type(-1)) return hole;
    for(; ; delta++) {
      size_type i = (h + delta) & mask;
      if(st[i] != State::FILLED) {
        prode = delta;
        return i;
      }
    }
  }

  void reserve(int next_cnt) {
    size_type required_cnt = next_cnt + (next_cnt >> 1) + 1;
    if(required_cnt > bck.size()) {
      next_cnt = 4;
      while(next_cnt < required_cnt) next_cnt <<= 1;
    }
    else if(next_cnt <= bck.size() / 4) {
      next_cnt = std::max(4, (int)bck.size() / 2);
    }
    else {
      return;
    }
    std::vector<State> old_st(next_cnt, State::INACTIVE);
    std::vector<pair_type> old_bck(next_cnt);

    std::swap(old_st, st);
    std::swap(old_bck, bck);
    mask = next_cnt - 1;
    sz = 0;
    prode = 0;
    min_elem = next_cnt - 1;


    for(size_type pos = 0; pos < old_bck.size(); pos++) {
      if(old_st[pos] == State::FILLED) {
        size_type i = find_empty(old_bck[pos].first);
        st[i] = State::FILLED;
        bck[i] = std::move(old_bck[pos]);
        min_elem = std::min(min_elem, i);
        sz += 1;
      }
    }
  }

  void insert(const key_type& key, const value_type& val) {
    reserve(sz + 1);
    size_type i = find_or_allocate(key);
    if(st[i] != State::FILLED) {
      st[i] = State::FILLED;
      bck[i] = pair_type(key, val);
      min_elem = std::min(min_elem, i);
      sz++;
    }
    else {
      bck[i] = pair_type(key, val);
    }
  }

  bool erase(const key_type& key) {
    size_type i = find_filled(key);
    if(i == size_type(-1)) {
      return false;
    }
    else {
      st[i] = State::ACTIVE;
      bck[i].~pair_type();
      sz--;
      return true;
    }
  }

  pair_type* get(const key_type& key) {
    size_type i = find_filled(key);
    if(i == size_type(-1)) {
      return nullptr;
    }
    else {
      return &bck[i];
    }
  }

  pair_type* get_or_insert(const key_type& key, const value_type val) {
    reserve(sz + 1);
    size_type i = find_or_allocate(key);
    if(st[i] != State::FILLED) {
      st[i] = State::FILLED;
      bck[i] = pair_type(key, val);
      min_elem = std::min(min_elem, i);
      sz++;
    }
    return &bck[i];
  }

  pair_type get_and_erase(const key_type& key) {
    size_type i = find_filled(key);
    st[i] = State::ACTIVE;
    pair_type p = std::move(bck[i]);
    sz--;
    return p;
  }


  template<class Func>
  void search_all(Func func) {
    for(size_type i = min_elem; i < bck.size(); i++) {
      if(st[i] == State::FILLED) {
        min_elem = i;
        size_type res = func(bck[i]);
        if(res & 0b10) {
          st[i] = State::ACTIVE;
          bck[i].~pair_type();
          sz--;
        }
        if(res & 0b01) {
           return;
        }
      }
    }
  }

  size_type size() const { return sz; }
};


struct Hashu32 {
  std::uint32_t operator()(std::uint32_t key) {
    int c2=0x27d4eb2d; // a prime or an odd constant
    key = (key ^ 61) ^ (key >> 16);
    key = key + (key << 3);
    key = key ^ (key >> 4);
    key = key * c2;
    key = key ^ (key >> 15);
    return key;
  }
};

struct Hashu64 {
  std::size_t operator()(std::uint64_t key) {
    key = (~key) + (key << 18); // key = (key << 18) - key - 1;
    key = key ^ (key >> 31);
    key = key * 21; // key = (key + (key << 2)) + (key << 4);
    key = key ^ (key >> 11);
    key = key + (key << 6);
    key = key ^ (key >> 22);
    return (int) key;
  }

};

Data Structures Other

うまく分類できなかったもの

SWAG

Spec

Code

#include <stack>

struct swag {

  using T = int;
  static T ide() {
    return 0;
  }
  static T ope(const T& a, const T& b) {
    return a + b;
  }

  std::stack<T> left_v, right_v, left_f, right_f;

  swag() {
    left_f.push(ide());
    right_f.push(ide());
  }

  void push(T val) {
    left_f.push(ope(left_f.top(), val));
    left_v.push(std::move(val));
  }

  T fold() {
    return ope(right_f.top(), left_f.top());
  }

  void pop() {
    if(right_f.size() == 1) {
      while(left_f.size() > 1) {
        right_f.push(ope(left_v.top(), right_f.top()));
        right_v.push(left_v.top());
        left_f.pop();
        left_v.pop();
      }
    }
    right_f.pop();
    right_v.pop();
  }
};

Fenwick Tree

Spec

Code

#include <vector>
using i64 = long long;

template<class AbelianMonoid, class Ope, const AbelianMonoid& Ide>
struct fenwick_tree {
  using value_type = AbelianMonoid;

  i64 n;
  std::vector<value_type> node;
  Ope ope;

  fenwick_tree(i64 n_): n(n_), node(n + 1, Ide) {}
  fenwick_tree(const std::vector<value_type>& init): n(init.size()), node(n + 1, Ide) {
    for(i64 i = 0;i < init.size(); i++) node[i + 1] = init[i];
    for(i64 i = 1;i < n;i++) node[i + (i & -i)] = ope(node[i + (i & -i)], node[i]);
  }
  void modify(i64 i, value_type x) {
    i++;
    while(i <= n) {
      node[i] = ope(node[i], x);
      i += (i & -i);
    }
  }
  // [0, i)
  value_type sum(i64 i) const {
    value_type ret = Ide;
    while(i > 0) {
      ret = ope(ret, node[i]);
      i -= i & (-i);
    }
    return ret;
  }
};

Online Dynamic Connectivity

Code

#include <cstdint>
#include <set>
#include <string>
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <cassert>

const int NODE_SIZE = 303030 * 6;
struct euler_tour_tree {
  using value_type = long long;
  using size_type = std::size_t;
  using node_index = std::int_least32_t;
  using vertex_index = std::int_least32_t;

  struct node;
  static struct node n[NODE_SIZE];
  static node_index ni;

  struct node {
    vertex_index s, d;
    node_index c[3];
    int sz;
    int flag;
    value_type val;
    value_type Sigma;
    node(): sz(1) {}
    inline node& operator[](size_type d) { return n[c[d]]; }
  };

  node_index new_edge(int s, int d, bool hi) {
    int i = ni++;
    int ri = ni++;
    n[i].s = n[ri].d = s;
    n[i].d = n[ri].s = d;
    n[i].sz = n[ri].sz = 0;
    n[i].flag = hi;
    return i;
  }

  static void fix(node_index i) {
    n[i].sz = (n[i].s == n[i].d) ? 1 : 0;
    if(n[i].c[0]) n[i].sz += n[i][0].sz;
    if(n[i].c[1]) n[i].sz += n[i][1].sz;
    n[i].flag &= 0b0101;
    n[i].flag |= n[i].flag << 1;
    if(n[i].c[0]) n[i].flag |= n[i][0].flag & 0b1010;
    if(n[i].c[1]) n[i].flag |= n[i][1].flag & 0b1010;
    n[i].Sigma = n[i].val;
    if(n[i].c[0]) n[i].Sigma += n[i][0].Sigma;
    if(n[i].c[1]) n[i].Sigma += n[i][1].Sigma;
  }

  static int child_dir(node_index i) {
    if(n[i].c[2]) {
      if(n[i][2].c[0] == i) { return 0; }
      else if(n[i][2].c[1] == i) { return 1; }
    }
    return 2;
  }

  static void rotate(node_index x, size_type dir) {
    node_index p = n[x].c[2];
    int x_dir = child_dir(x);
    node_index y = n[x].c[dir ^ 1];
    if(n[y].c[dir]) n[y][dir].c[2] = x;
    n[x].c[dir ^ 1] = n[y].c[dir];
    n[n[x].c[2] = y].c[dir] = x;
    n[y].c[2] = p;
    if(x_dir < 2) n[p].c[x_dir] = y;
    if(n[x].c[dir ^ 1]) fix(n[x].c[dir ^ 1]);
    fix(x);
  }

  static void splay(node_index i) {
    int i_dir;
    int j_dir;
    while((i_dir = child_dir(i)) < 2) {
      node_index j = n[i].c[2];
      if((j_dir = child_dir(j)) < 2) {
        node_index k = n[j].c[2];
        if(i_dir == j_dir) rotate(k, j_dir ^ 1), rotate(j, i_dir ^ 1);
        else rotate(j, i_dir ^ 1), rotate(k, j_dir ^ 1);
      }
      else rotate(j, i_dir ^ 1);
    }
    fix(i);
  }

  static node_index merge_back(node_index l, node_index r) {
    if(!l) return r;
    if(!r) return l;
    while(n[l].c[1]) l = n[l].c[1];
    splay(l);
    n[n[r].c[2] = l].c[1] = r;
    fix(l);
    return l;
  }

  static std::pair<node_index, node_index> split(node_index i) {
    splay(i);
    node_index l = n[i].c[0];
    n[i].c[0] = n[l].c[2] = 0;
    fix(i);
    return { l, i };
  }

  static void reroot(node_index v) {
    auto p = split(v);
    merge_back(p.second, p.first);
    splay(v);
  }

  static bool same_root(node_index i, node_index j) {
    if(i) splay(i);
    if(j) splay(j);
    while(n[i].c[2]) i = n[i].c[2];
    while(n[j].c[2]) j = n[j].c[2];
    return i == j;
  }

  node_index n_start;
  std::unordered_map<long long, node_index> emp;
  euler_tour_tree() {}
  euler_tour_tree(int N): n_start(ni) {
    ni += N;
    for(int i = 0; i < N; i++) {
      n[i + n_start].s = n[i + n_start].d = i;
    }
  }

  bool edge_exist(vertex_index x, vertex_index y) {
    if(x > y) std::swap(x, y);
    return emp.count(((long long)x << 32) | (long long)y);
  }

  void link(vertex_index x, vertex_index y, bool hi) {
    if(x > y) std::swap(x, y);
    int ei = new_edge(x, y, hi);
    assert(!emp.count(((long long)x << 32) | (long long)y));
    emp[((long long)x << 32) | (long long)y] = ei;
    x += n_start;
    y += n_start;
    reroot(x);
    reroot(y);
    n[n[x].c[2] = ei].c[0] = x;
    n[n[y].c[2] = ei].c[1] = y;
    fix(ei);
    merge_back(ei, ei + 1);
  }

  void cut(vertex_index x, vertex_index y) {
    if(x > y) std::swap(x, y);
    auto iter = emp.find(((long long)x << 32) | (long long)y);
    int ei = iter->second;
    int rei = ei + 1;
    emp.erase(iter);

    auto p = split(ei);
    if(p.first && same_root(p.first, rei)) {
      auto q = split(rei);
      node_index left = q.first;
      node_index center = n[q.second].c[1];
      node_index right = n[p.second].c[1];
      n[center].c[2] = 0;
      n[right].c[2] = 0;
      merge_back(left, right);
    }
    else {
      splay(ei);
      ei = n[ei].c[1];
      n[ei].c[2] = 0;
      auto q = split(rei);
      splay(p.first);
      node_index left = p.first;
      node_index center = q.first;
      node_index right = n[q.second].c[1];
      n[right].c[2] = 0;
      merge_back(left, right);
    }
  }

  bool same_tree(vertex_index x, vertex_index y) {
    return same_root(x + n_start, y + n_start);
  }

  int tree_size(vertex_index x) {
    x += n_start;
    splay(x);
    return n[x].sz;
  }

  void subedge_set(vertex_index x, bool val) {
    x += n_start;
    splay(x);
    if(val) n[x].flag |= (0b0100);
    else n[x].flag &= ~(0b0100);
    fix(x);
  }

  void add_val(vertex_index x, value_type val) {
    x += n_start;
    splay(x);
    n[x].val += val;
    fix(x);
  }
  value_type tree_sum(vertex_index x) {
    x += n_start;
    splay(x);
    return n[x].Sigma;
  }

  template<class Func>
  void hilevel_edges(vertex_index v, Func f) {
    node_index i = v + n_start;
    splay(i);
    while(i && (n[i].flag & 0b0010)) {
      while(1) {
        if(n[i].flag & 0b0001) {
          f(n[i].s, n[i].d);
          splay(i);
          n[i].flag &= ~(0b0001);
          fix(i);
          break;
        }
        else if(n[i].c[0] && (n[i][0].flag & 0b0010)) i = n[i].c[0];
        else i = n[i].c[1];
      }
    }
  }
  template<class Func>
  int subedges(vertex_index v, Func f) {
    node_index i = v + n_start;
    splay(i);
    while(i && (n[i].flag & 0b1000)) {
      while(1) {
        if(n[i].flag & 0b0100) {
          if(f(n[i].s)) {
            return 1;
          }
          splay(i);
          break;
        }
        else if(n[i].c[0] && (n[i][0].flag & 0b1000)) i = n[i].c[0];
        else i = n[i].c[1];
      }
    }
    return 0;
  }


  void debug_tree(node_index i, std::string indent) {
    if(n[i].c[0]) {
      debug_tree(n[i].c[0], indent + "l");
    }
    std::cout << " " << i << " = (" << n[i].s << " " << n[i].d << ")" << " p " << n[i].c[2] << std::endl;
    if(n[i].c[1]) {
      debug_tree(n[i].c[1], indent + "r");
    }
  }
};

euler_tour_tree::node_index euler_tour_tree::ni = 1;
euler_tour_tree::node euler_tour_tree::n[NODE_SIZE];

struct online_dynamic_connectivity {
  int N;
  std::vector<euler_tour_tree> ett;
  std::vector<std::vector<std::unordered_set<int>>> E;

  online_dynamic_connectivity(int N): N(N) {
    ett.emplace_back(N);
    E.emplace_back(N);
  }

  void link(int x, int y) {
    if(ett[0].same_tree(x, y)) {
      if(E[0][x].size() == 0) ett[0].subedge_set(x, 1);
      if(E[0][y].size() == 0) ett[0].subedge_set(y, 1);
      E[0][x].insert(y);
      E[0][y].insert(x);
    }
    else {
      ett[0].link(x, y, true);
    }
  }

  void replace(int x, int y, int level) {
    for(int k = 0; k < level; k++) {
      ett[k].cut(x, y);
    }
    for(int k = level; k --> 0;) {
      if(ett[k].tree_size(x) > ett[k].tree_size(y)) std::swap(x, y);
      ett[k].hilevel_edges(x, [&](int s, int d) { ett[k + 1].link(s, d, true); });
      int res = ett[k].subedges(x, [&](int s) {
        for(auto iter = E[k][s].begin(); iter != E[k][s].end(); ) {
          int d = *iter;
          iter = E[k][s].erase(iter);
          E[k][d].erase(s);
          if(E[k][s].size() == 0) ett[k].subedge_set(s, 0);
          if(E[k][d].size() == 0) ett[k].subedge_set(d, 0);
          if(ett[k].same_tree(s, d)) {
            if(E[k + 1][s].size() == 0) ett[k + 1].subedge_set(s, 1);
            if(E[k + 1][d].size() == 0) ett[k + 1].subedge_set(d, 1);
            E[k + 1][s].insert(d);
            E[k + 1][d].insert(s);
          }
          else {
            for(int kk = k + 1; kk --> 0;) {
              ett[kk].link(s, d, kk == k);
            }
            return 1;
          }
        }
        return 0;
        });
      if(res) return;
    }
  }

  void cut(int x, int y) {
    for(int k = 0; k < ett.size(); k++) {
      if(E[k][x].count(y)) {
        E[k][x].erase(y);
        E[k][y].erase(x);
        if(E[k][x].size() == 0) ett[k].subedge_set(x, 0);
        if(E[k][y].size() == 0) ett[k].subedge_set(y, 0);
        return;
      }
    }
    for(int k = ett.size(); k --> 0;) {
      if(ett[k].edge_exist(x, y)) {
        if(k + 1 == ett.size()) {
          ett.emplace_back(N);
          E.emplace_back(N);
        }
        replace(x, y, k + 1);
      }
    }
  }
  void add_val(int x, long long val) {
    ett[0].add_val(x, val);
  }
  int size(int x) {
    return ett[0].tree_size(x);
  }
  long long sum(int x) {
    return ett[0].tree_sum(x);
  }
  bool same(int x, int y) {
    return ett[0].same_tree(x, y);
  }
};

Range Valued Array

[, r)のmapとして管理する範囲set可能な配列

Spec

  • (constructor)(value_type init_value)
    • init_value: 全域をこの値で初期化する
  • iterator_type range_set_value(key_type l, ley_type r, const value_type& val)
    • [l, r)valで初期化する
  • operator[](const key_type& k)
    • インデックスkの値を返す

Code

#include <map>
#include <iostream>

struct range_valued_array {
  using key_type = int;
  using value_type = long long;
  using iterator_type = std::map<key_type, value_type>::iterator;
  
  // [, r)
  std::map<key_type, value_type> mp;
  key_type start;
  value_type init_value;
  

  range_valued_array(key_type start = key_type(), value_type init_value = value_type()): start(start), init_value(init_value) {}

  // key of return iterator is `pos`
  iterator_type inner_split(key_type pos) {
    if(pos == start) return mp.begin();
    auto iter = mp.lower_bound(pos);
    if(iter->first == pos) return iter;
    mp[pos] = iter != mp.end() ? iter->second : init_value;
    return --iter;
  }

  iterator_type range_set_value(key_type l, key_type r, const value_type& val) {
    if(l == r) return mp.end();
    auto liter = inner_split(l);
    auto riter = inner_split(r);
    std::cout << liter->first << " " << riter->first << std::endl;
    mp.erase(++liter, riter);
    riter->second = val;
    return riter;
  }

  iterator_type begin() {
    return mp.begin();
  }
  iterator_type end() {
    return mp.end();
  }

  value_type& operator[](const key_type& k) {
    auto iter = mp.upper_bound(k);
    return iter == mp.end() ? init_value : iter->second;
  }

  void debug_print() {
    std::cout << "[";
    for(auto p: mp) {
      std::cout << p.first << ": " << p.second << ", ";
    }
    std::cout << "]" << std::endl;
  }
};

Math

数学系を

modint

xxで割ったあまりを求めよで使える構造体

Spec

  • template

    • i64 M
      • \( \mod M \)の剰余環
  • modint(const i64 x = 0)

    • \( x \mod M \)で初期化
  • value()

    • \( x \mod M \)を返す
  • pow(i64 r)

    • \( x^r \mod M \)を返す
  • +, -, *, /

Code

#include <iostream>
using i64 = long long;
template<i64 M> struct modint { i64 a;
  constexpr modint(const i64 x = 0): a((x%M+M)%M){}
  constexpr i64 value() const { return a; }
  constexpr modint inv() const { return this->pow(M-2); }
  constexpr modint pow(i64 r) const {
    modint ans(1); modint aa = *this;
    while(r) { if(r & 1) ans *= aa; aa *= aa; r >>= 1; }
    return ans;
  }
  constexpr bool operator==(const modint& r) const { return a == r.a; }
  constexpr bool operator!=(const modint& r) const { return a != r.a; }
  constexpr modint& operator=(const i64 r) { a = (r % M + M) % M; return *this; }
  constexpr modint& operator+=(const modint r) { a += r.a; if(a >= M) a -= M; return *this; }
  constexpr modint& operator-=(const modint r) { a -= r.a; if(a < 0) a += M; return *this; }
  constexpr modint& operator*=(const modint r) { a = a * r.a % M; return *this; }
  constexpr modint& operator/=(const modint r) { (*this) *= r.inv(); return *this; }
  constexpr modint operator+(const modint r) const { return modint(*this) += r; }
  constexpr modint operator-(const modint r) const { return modint(*this) -= r; }
  constexpr modint operator-() const { return modint(0) - modint(*this); }
  constexpr modint operator*(const modint r) const { return modint(*this) *= r; }
  constexpr modint operator/(const modint r) const { return modint(*this) /= r; }
  constexpr bool operator!=(const modint r) const { return this->value() != r.value(); }
};

template<const i64 M> std::ostream& operator<<(std::ostream& os, const modint<M>& m) { os << m.value(); return os; }


montgomery modint

乗算がはやーいmodint

Code

#include <iostream>

template<uint32_t M>
struct montgomery_modint {
  using Self = montgomery_modint<M>;
  using i32 = int32_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 res = M;
    for(int i = 0; i < 4; i++) res *= 2 - M * res;
    return res;
  }
  static constexpr u32 reduce(u64 a) {
    return (a + u64(u32(a) * u32(-r)) * M) >> 32;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(M) % M;

  u32 a;

  constexpr montgomery_modint() : a(0) {}
  constexpr montgomery_modint(int64_t a) : a(reduce(u64(a % M + M) * n2)) {}

  constexpr u32 val() const {
    u32 res = reduce(a);
    return res >= M ? res - M : res;
  }
  constexpr Self pow(u64 r) const {
    Self ans(1); Self aa = *this;
    while(r) { if(r & 1) ans *= aa; aa *= aa; r >>= 1; }
    return ans;
  }
  constexpr Self inv() const { return this->pow(M - 2); }
  constexpr Self& operator+=(const Self& r) {
    if(i32(a += r.a - 2 * M) < 0) a += 2 * M;
    return *this;
  }
  constexpr Self& operator-=(const Self& r) {
    if(i32(a -= r.a) < 0) a += 2 * M;
    return *this;
  }
  constexpr Self& operator*=(const Self& r) {
    a = reduce(u64(a) * r.a);
    return *this;
  }
  constexpr Self& operator/=(const Self& r) {
    *this *= r.inv();
    return *this;
  }
  constexpr Self operator+(const Self r) const { return Self(*this) += r; }
  constexpr Self operator-(const Self r) const { return Self(*this) -= r; }
  constexpr Self operator-() const { return Self() - Self(*this); }
  constexpr Self operator*(const Self r) const { return Self(*this) *= r; }
  constexpr Self operator/(const Self r) const { return Self(*this) /= r; }
  constexpr bool operator==(const Self& r) const {
    return (a >= M ? a - M : a) == (r.a >= M ? r.a - M : r.a);
  }
  constexpr bool operator!=(const Self& r) const {
    return (a >= M ? a - M : a) == (r.a >= M ? r.a - M : r.a);
  }
};

template<uint32_t M>
std::ostream& operator<<(std::ostream& os, const montgomery_modint<M>& m) {
  return os << m.val();
}
template<uint32_t M>
std::istream& operator>>(std::istream& is, montgomery_modint<M>& m) {
  int64_t t;
  is >> t;
  m = montgomery_modint<M>(t);
  return is;
}

template<uint32_t mod>
using modint = montgomery_modint<mod>;

Matrix

行列演算

Code

#include <vector>
using namespace std;
using i64 = long long;

template<class T>
struct matrix {
  int N, M;
  vector<vector<T>> mat;
  matrix(int N, int M, T init = T::zero()): N(N), M(M), mat(N, vector<T>(M, init)) {}
  vector<T>& operator[](i64 i) { return mat[i]; }
  const vector<T>& operator[](i64 i) const { return mat[i]; }
  static matrix<T> E(int N) {
    matrix<T> mat(N, N, T::zero());
    for(int i = 0; i < N; i++) {
      mat[i][i] = T::one();
    }
    return mat;
  }
};

template<class T>
matrix<T> operator+(const matrix<T>& lm, const matrix<T>& rm) {
  matrix ret(lm.N, lm.M);
  for(int i = 0; i < lm.N; i++) {
    for(int j = 0; j< lm.M; j++) {
      ret[i][j] = lm[i][j] + rm[i][j];
    }
  }
  return ret;
}
template<class T>
matrix<T> operator*(const matrix<T>& lm, const matrix<T>& rm) {
  matrix<T> ret(lm.N, rm.M);
  for(int i = 0; i < lm.N; i++) {
    for(int j = 0; j < lm.M; j++) {
      for(int k = 0; k < rm.M; k++) {
        ret[i][k] = ret[i][k] + lm[i][j] * rm[j][k];
      }
    }
  }
  return ret;
}

template<class T>
matrix<T> operator*(const matrix<T>& lm, const T& r) {
  matrix<T> ret(lm.N, lm.M);
  for(int i = 0; i < lm.N; i++) {
    for(int j = 0; j< lm.M; j++) {
      ret[i][j] = lm[i][j] * r;
    }
  }
  return ret;
}
 
template<class T>
matrix<T> matrix_pow(matrix<T> mat, i64 r) {
  matrix<T> ret = matrix<T>::E(mat.N);
  while(r > 0) {
    if(r & 1) ret = ret * mat;
    mat = mat * mat;
    r >>= 1;
  }
  return ret;
}

Chinese Remainder Theorem

Code

#include <tuple>
#include <vector>
std::tuple<long long, long long, long long> ext_gcd(long long a, long long b) {
  if(b == 0ll) {
    return std::tuple<long long, long long, long long>{ a, 1ll, 0ll };
  }
  else {
    auto [g, q, p] = ext_gcd(b, a%b);
    return std::tuple<long long, long long, long long>{ g, p, q-a/b*p };
  }
  ;
}
std::tuple<long long, long long> chinese_remainder_theorem(std::vector<std::tuple<long long, long long>> const& tms) {
  long long r = 0ll;
  long long M = 1ll;
  for(auto [t, m]: tms) {
    auto [g, p, q] = ext_gcd(M, m);
    if((t-r)%g != 0ll) {
      return std::tuple<long long, long long>{ 0ll, -1ll };
    }
    ;
    long long tmp = (t-r)/g*p%(m/g);
    r = r+M*tmp;
    M = M*(m/g);
  };
  return std::tuple<long long, long long>{ (r%M+M)%M, M };
}

Garner's Algorithm

FPSで使う

Spec

\(m_0, \cdots, m_{k-1} \)が互いに素とする.
\( x < \prod{m_i} \)を満たす\( x \)について, \( x \mod m_i \)がわかっている時,
\( O(k^2 + k \log m) \)で \( x \mod M \) を求めることができる.

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

i64 pow_mod(i64 x, i64 r, i64 mod) {
  i64 ans = 1;
  while(r) {
    if(r & 1) ans = (ans * x) % mod;
    r >>= 1;
    x = x * x % mod;
  }
  return ans;
}

i64 inv_mod(i64 x, i64 mod) {
  return pow_mod(x, mod - 2, mod);
}

i64 garner(const vector<i64> &x, vector<i64> mods, i64 mod) {
  mods.emplace_back(mod);
  vector<i64> coeffs(x.size() + 1, 1);
  vector<i64> constants(x.size() + 1, 0);
  for(i64 i = 0; i < x.size(); i++) {
    i64 v = (x[i] - constants[i]) * inv_mod(coeffs[i], mods[i]) % mods[i];
    if(v < 0) v += mods[i];
    for(i64 j = i + 1; j < x.size() + 1; j++) {
      constants[j] = (constants[j] + coeffs[j] * v) % mods[j];
      coeffs[j] = (coeffs[j] * mods[i]) % mods[j];
    }
  }
  return constants.back();
}

Formal Power Series

FPS

FPS

  • resize: リサイズ
  • resized: リサイズしたFPSを返す
  • +=, -=, +, -: 加減法、次数の大きい方にリサイズされる。
  • *, *=: 定数倍
  • DFT dft(int n): 長さnにリサイズしてdft
  • inv(int n): \(f * g \equiv 1 \pmod{x^n}\)となるような\(g\)を返す
  • diff(int n): \(f' \bmod{x^n}\)
  • integral(int n): \(\int f dx \bmod{x^n}\)
  • log(int n): \(\log f \bmod{x^n}\)
  • exp(int n): \(\exp f \bmod{x^n}\)

DFT

  • +=, -=, +, -: 加減法 線形性より
  • *, *=: 定数倍 線形性より
  • *: 畳み込み
  • idft(int n = -1): idftした後に長さnでリサイズ 指定しなければそのまま
#include <vector>
using i64 = long long;

std::size_t bound_pow2(std::size_t sz) {
  return 1ll << (std::__lg(sz - 1) + 1);
}

template<class fps_multiply>
struct FPS {
  struct DFT {
    using C = typename fps_multiply::conv_type;
    std::vector<C> coef;
    DFT(std::vector<C> c): coef(std::move(c)) {}
    DFT clone() const {
      return DFT(coef);
    }
    std::size_t size() const {
      return coef.size();
    }
    C& operator[](int i) {
      return coef[i];
    }
    const C& operator[](int i) const {
      return coef[i];
    }
    DFT& operator+=(const DFT& r) {
      assert(coef.size() == r.size());
      for(int i = 0;i < coef.size(); i++) {
        coef[i] += r[i];
      }
      return (*this);
    }
    DFT& operator-=(const DFT& r) {
      assert(coef.size() == r.size());
      for(int i = 0;i < coef.size(); i++) {
        coef[i] -= r[i];
      }
      return (*this);
    }
    DFT& operator*=(const DFT& r) {
      assert(coef.size() == r.size());
      for(int i = 0;i < coef.size(); i++) {
        coef[i] *= r[i];
      }
      return (*this);
    }
    DFT& operator*=(const C& t) {
      for(int i = 0; i < coef.size(); i++) {
        coef[i] *= t;
      }
      return (*this);
    }
    DFT operator+(const DFT& r) const { return DFT(*this) += r; }
    DFT operator-(const DFT& r) const { return DFT(*this) -= r; }
    DFT operator*(const DFT& r) const { return DFT(*this) *= r; }
    DFT operator*(const C& r) const { return DFT(*this) *= r; }
    FPS idft(int n = -1) && {
      auto res = fps_multiply::idft(std::move(coef));
      if(n > 0) {
        res.resize(n);
      }
      return FPS(std::move(res));
    }
  };
  using T = typename fps_multiply::fps_type;
  std::vector<T> coef;
  FPS(std::vector<T> f): coef(std::move(f)) {}
  void resize(int n) { coef.resize(n, T(0)); }
  FPS resized(int n) const {
    std::vector<T> res(n);
    for(int i = 0;i < n && i < coef.size();i++) res[i] = coef[i];
    return FPS(std::move(res));
  }
  FPS clone() const {
    return FPS(coef);
  }
  std::size_t size() const {
    return coef.size();
  }
  T& operator[](int i) {
    return coef[i];
  }
  const T& operator[](int i) const {
    return coef[i];
  }
  FPS& operator+=(const FPS& r) {
    if(coef.size() < r.size()) coef.resize(r.size());
    for(int i = 0;i < coef.size() && i < r.size(); i++) {
      coef[i] += r[i];
    }
    return (*this);
  }
  FPS& operator-=(const FPS& r) {
    if(coef.size() < r.size()) coef.resize(r.size());
    for(int i = 0;i < coef.size() && i < r.size(); i++) {
      coef[i] -= r[i];
    }
    return (*this);
  }
  FPS& operator*=(const T& t) {
    for(int i = 0; i < coef.size(); i++) {
      coef[i] *= t;
    }
  }
  FPS operator+(const FPS& r) const { return FPS(*this) += r; }
  FPS operator-(const FPS& r) const { return FPS(*this) -= r; }
  FPS operator*(const T& r) const { return FPS(*this) *= r; }
  DFT dft(int n) && {
    assert(!(n & (n - 1)));
    coef.resize(n);
    return DFT(fps_multiply::dft(std::move(coef)));
  }

  FPS inv(int n) const {
    FPS g = FPS(std::vector<T>{ T(1) / (*this)[0] });
    for(int i = 1;i < n;i <<= 1) {
      DFT gdft = g.resized(i << 1).dft(i << 1);
      FPS e = (gdft * this->resized(i << 1).dft(i << 1)).idft();
      for(int j = 0;j < i;j++) {
        e[j] = T(0);
        e[j + i] = e[j + i] * T(-1);
      }
      FPS f = std::move(gdft *= std::move(e).dft(i << 1)).idft();
      for(int j = 0;j < i;j++) {
        f[j] = g[j];
      }
      g = std::move(f);
    }
    g.resize(n);
    return g;
  }

  FPS diff(int n) const {
    std::vector<T> res(n);
    for(int i = 0; i + 1 < this->size() && i < n; i++) {
      res[i] = coef[i + 1] * T(i + 1);
    }
    return FPS(std::move(res));
  }

  FPS integral(int n) const {
    std::vector<T> res(n);
    int m = std::min(n, int(coef.size() + 1));
    res[0] = T(1);
    for(int i = 1; i < m; i++) {
      res[i] = res[i - 1] * T(i);
    }
    T finv = T(1) / res[m - 1];
    for(int i = m; i --> 1;) {
      res[i] = coef[i - 1] * finv * res[i - 1];
      finv *= T(i);
    }
    res[0] = T(0);
    return FPS(std::move(res));
  }

  // F(0) must not be 0
  FPS log(int n) const {
    FPS in = this->inv(n);
    FPS di = this->diff(n);
    int m = bound_pow2(n);
    return (std::move(di).dft(m * 2) * std::move(in).dft(m * 2)).idft().integral(n);
  }

  FPS exp(int n) const {
    FPS f(std::vector<T>{ T(1) });
    for(i64 i = 1;i < n;i <<= 1 ) {
      FPS flog = f.log(i << 1);
      for(int j = 0; j < (i << 1); j++) {
        flog[j] = (j < coef.size() ? coef[j] - flog[j] : -flog[j]);
      }
      flog[0] += T(1);
      f = (std::move(f).dft(i << 2) * std::move(flog).dft(i << 2)).idft(i << 1);
    }
    f.resize(n);
    return f;
  }
};

NTTをそのまま使うとき

#include "modint.hpp"
#include "convolution/number_theoretic_transform.hpp"

template<const i64 mod, const i64 primitive>
struct fps_ntt_multiply {
  using fps_type = modint<mod>;
  using conv_type = modint<mod>;
  static std::vector<conv_type> dft(std::vector<fps_type> arr) {
    return number_theoretic_transform<mod, primitive>(std::move(arr));
  }
  static std::vector<fps_type> idft(std::vector<conv_type> arr) {
    return inverse_number_theoretic_transform<mod, primitive>(std::move(arr));
  }
  static std::vector<conv_type> multiply(std::vector<conv_type> a, const std::vector<conv_type>& b) {
    for(int i = 0;i < a.size();i++) a[i] *= b[i];
    return a;
  }
  static std::vector<conv_type> self_multiply(std::vector<conv_type> a) {
    for(int i = 0;i < a.size();i++) a[i] *= a[i];
    return a;
  }
};

任意modで

#include "modint.hpp"
#include "convolution/number_theoretic_transform.hpp"
#include "garner.hpp"
#include <vector>
#include <tuple>

template<i64 M, i64... NTTis>
struct fps_multiply_arb {
  using fps_type = std::vector<modint<M>>;
  using conv_type = std::tuple<std::vector<modint<NTT_PRIMES[NTTis][0]>>...>;
  const static std::size_t tsize = std::tuple_size<conv_type>::value;

  template<i64 M2, i64 primitive>
  static std::vector<modint<M2>> dft_m2(const fps_type& arr) {
    std::vector<modint<M2>> res(arr.size());
    for(std::size_t i = 0; i < arr.size(); i++)
      res[i] = modint<M2>(arr[i].value());
    return number_theoretic_transform<M2, primitive>(std::move(res));
  }
  template<i64 M2, i64 primitive>
  static std::vector<modint<M2>> idft_m2(std::vector<modint<M2>> arr) {
    return inverse_number_theoretic_transform<M2, primitive>(std::move(arr));
  }
  template<std::size_t... I>
  static fps_type idft_func(std::index_sequence<I...>, conv_type arr) {
    arr = std::make_tuple(idft_m2<NTT_PRIMES[NTTis][0], NTT_PRIMES[NTTis][1]>(std::get<I>(arr))...);
    std::size_t len = std::get<0>(arr).size();
    static std::vector<i64> primes = { NTT_PRIMES[NTTis][0]... };
    fps_type res(len);
    for(std::size_t i = 0; i < len; i++) {
      std::vector<i64> x = { std::get<I>(arr)[i].value()... };
      res[i] = modint<M>(garner(x, primes, M));
    }
    return std::move(res);
  }
  template<i64 M2>
  static char mult_m2(std::vector<modint<M2>>& a, const std::vector<modint<M2>>& b) {
    for(int i = 0;i < a.size();i++) a[i] *= b[i];
    return 0;
  }
  template<std::size_t... I>
  static void mult_func(std::index_sequence<I...>, conv_type& a, const conv_type& b) {
    auto res = std::make_tuple(mult_m2<NTT_PRIMES[NTTis][0]>(std::get<I>(a), std::get<I>(b))...);
  }
  static conv_type dft(fps_type arr) {
    return std::make_tuple(dft_m2<NTT_PRIMES[NTTis][0], NTT_PRIMES[NTTis][1]>(arr)...);
  }
  static fps_type idft(conv_type arr) {
    return idft_func(std::make_index_sequence<tsize>(), std::move(arr));
  }
  static conv_type multiply(conv_type a, const conv_type& b) {
    mult_func(std::make_index_sequence<tsize>(), a, b);
    return a;
  }
  static conv_type self_multiply(conv_type a) {
    mult_func(std::make_index_sequence<tsize>(), a, a);
    return a;
  }
};

Mori FPS Division

FPS Division

  • \( P(x) / Q(x) \) の \( [x^N] \)の係数を求める
  • 時間計算量は \( O( k \log k \log N) \)
  • \( P(x) \)は高々\( k - 1 \)次の多項式
  • \( Q(x) \)は\( k \)次の多項式

Code

#include "formal_power_series.hpp"

template<class fps_multiply>
typename fps_multiply::fps_type mori_fps_division(FPS<fps_multiply> P, FPS<fps_multiply> Q, std::size_t N) {
  using T = typename fps_multiply::fps_type;
  i64 k = bound_pow2(std::max(P.size(), Q.size()));
  P.resize(k * 2);
  Q.resize(k * 2);
  while(N > 0) {
    auto Qm = Q;
    for(std::size_t i = 1; i < Qm.size(); i += 2) {
      Qm[i] *= T(-1);
    }
    //auto Pd = fps_multiply::dft(std::move(P.coef));
    //auto Qd = fps_multiply::dft(std::move(Q.coef));
    //auto Qmd = fps_multiply::dft(std::move(Qm.coef));
    auto Pd = std::move(P).dft(k * 2);
    auto Qd = std::move(Q).dft(k * 2);
    auto Qmd = std::move(Qm).dft(k * 2);
    //auto PQ = fps_multiply::idft(
    //    fps_multiply::multiply(std::move(Pd), Qmd)
    //    );
    auto PQ = std::move(Pd *= Qmd).idft();
    //auto QQ = fps_multiply::idft(
    //    fps_multiply::multiply(std::move(Qd), std::move(Qmd))
    //    );
    auto QQ = std::move(Qd *= Qmd).idft();
    {
      std::size_t i = 0;
      for(std::size_t j = 0; j < QQ.size(); j += 2) {
        QQ[i++] = QQ[j];
      }
      while(i < QQ.size()) QQ[i++] = T(0);
    }
    {
      std::size_t i = 0;
      for(std::size_t j = N % 2; j < PQ.size(); j += 2) {
        PQ[i++] = PQ[j];
      }
      while(i < PQ.size()) PQ[i++] = T(0);
    }
    P = std::move(PQ);
    Q = std::move(QQ);
    N /= 2;
  }
  return P[0] / Q[0];
}

#include "fps_ntt_multiply.hpp"
#include <iostream>

int main() {
  std::cin.tie(nullptr);
  std::ios::sync_with_stdio(false);
  using fp = modint<998244353>;
  using mlt = fps_ntt_multiply<998244353, 3>;
  using fps = FPS<mlt>;

  i64 D, K;
  std::cin >> D >> K;

  vector<fp> a(D);
  for(int i = 0;i < D;i++) std::cin >> a[i].a;
  vector<fp> c(D + 1);
  for(int i = 1; i <= D; i++) std::cin >> c[i].a;
  c[0] = fp(-1);

  fps A(std::move(a));
  fps Q(std::move(c));
  i64 d = bound_pow2(D + 1);
  fps P = std::move(Q.clone().dft(d) *= std::move(A).dft(d)).idft(D);
  std::cout << mori_fps_division<mlt>(std::move(P), std::move(Q), K).a << std::endl;
}

modint

Code

#include <vector>

template<class T>
std::vector<T> newton_interpolation(std::vector<std::pair<T, T>> ps) {
  std::vector<T> diff(ps.size());
  diff[0] = ps[0].second;
  std::vector<T> x(ps.size()), y(ps.size());
  for(int i = 0;i < ps.size(); i++) {
    x[i] = ps[i].first, y[i] = ps[i].second;
  }
  for(int i = 1;i < ps.size();i++) {
    std::vector<T> d(ps.size() - i);
    for(int j = 0;j + i < ps.size(); j++) {
      d[j] = (y[j + 1] - y[j]) / (x[j + i] - x[j]);
    }
    diff[i] = d[0];
    swap(y, d);
  }
 
  std::vector<T> ans(ps.size());
 
  std::vector<T> c(ps.size() + 1);
  c[0] = 1;
 
 
  for(int i = 0;i < ps.size();i++) {
    for(int j = 0;j < ps.size();j++) {
      ans[j] += diff[i] * c[j];
    }
    if(i + 1 == ps.size()) break;
 
    std::vector<T> next(ps.size() + 1);
    for(int j = 0;j < ps.size();j++) {
      next[j + 1] = c[j];
    }
    for(int j = 0;j < ps.size();j++) {
      next[j] -= c[j] * ps[i].first;
    }
    swap(c, next);
  }
  return ans;
}

Seidel's LP

Seidel's LP algorithm

  • LPを解くアルゴリズム
  • LP(線形計画問題)とは, 以下のように記述できる.

\[ \begin{align} \text{maximize} \ & c^T x \\ \text{subject to} \ & A x \leq b \\ \ & x \geq 0 \end{align} \]

  • この \( x \) を返す.
  • 計算量 \( O(d! m) ( d \text{は次元数}, m \text{は条件数}) \)

Arguments

matは, \( (A \ b) \)である.

boundsには, \( x \)のとる下界と上界を与える.

Memo

epsを変える必要があるかもしれない.

Code

#include <vector>
#include <set>
#include <cassert>
#include <limits>
#include <cmath>
#include <iostream>

/**
 * # Seidel's LP algorithm
 * - LPを解くアルゴリズム
 * - LP(線形計画問題)とは, 以下のように記述できる.
 * 
 * \\[
 * \begin{align}
 * \text{maximize} \ & c^T x \\\\
 * \text{subject to} \ & A x \leq b \\\\
 *                   \ & x \geq 0
 * \end{align}
 * \\]
 *
 * - この \\( x \\) を返す.
 * - 計算量 \\( O(d! m) ( d \text{は次元数}, m \text{は条件数}) \\)
 * 
 * ## Arguments 
 *
 * ```cpp
 * @3@
 * ```
 * `mat`は, \\( (A \ b) \\)である.
 *
 * ```cpp
 * @4@
 * ```
 * `bounds`には, \\( x \\)のとる下界と上界を与える.
 *
 * ## Memo
 *
 * ```cpp
 * @5@
 * ```
 * `eps`を変える必要があるかもしれない.
 **/
template<class R, class T = long double>
std::vector<T> seidel_lp(R& rnd, std::size_t d,
    const std::vector<T>& c,
    const std::vector<std::vector<T>>& mat,
    const std::vector<std::pair<T, T>>& bounds) {
  const static T eps = std::numeric_limits<T>::epsilon();
  const static auto eps_eq = [&](const T& a, const T& b) -> bool {
    return std::abs(a - b) <= eps;
  };
  assert(d > 0);
  if(d == 1) {
    assert(c.size() == 1);
    T low = bounds[0].first;
    T high = bounds[0].second;
    T z = T(0);
    for(const auto& a: mat) {
      assert(a.size() == 2);
      if(eps_eq(a[0], T(0))) {
        // equal
        if(std::abs(a[1] - z) <= eps || a[1] < z) z = a[1];
      }
      else if(a[0] > T(0)) {
        // greater
        T pa = a[1] / a[0];
        if(eps_eq(pa, high) || pa < high) high = pa;
      }
      else {
        T pa = a[1] / a[0];
        if(eps_eq(pa, low) || pa > low) low = pa;
      }
    }
    if(z < T(0) || high < low) return std::vector<T>();
    else if(eps_eq(c[0], T(0)) || c[0] > T(0)) return std::vector<T> { high };
    else return std::vector<T> { low };
  }
  else if(mat.empty()) {
    std::vector<T> res(d);
    for(int i = 0; i < d; i++) {
      if(eps_eq(c[i], T(0)) || c[i] > T(0)) res[i] = bounds[i].second;
      else res[i] = bounds[i].first;
    }
    return res;
  }
  else {
    int rmi = rnd() % mat.size();
    const auto& a = mat[rmi];
    std::vector<std::vector<T>> next_mat(mat.size() - 1);
    {
      int ni = 0;
      for(int i = 0; i < mat.size(); i++) {
        if(i == rmi) continue;
        next_mat[ni++] = mat[i];
      }
    }
    auto v = seidel_lp(rnd, d, c, next_mat, bounds);
    if(v.empty()) return v;
    {
      T value = T(0);
      for(int i = 0; i < d; i++) {
        value += a[i] * v[i];
      }
      if(eps_eq(value, a[d]) || value < a[d]) return v;
    }
    int k = -1;
    for(int i = 0; i < d; i++) {
      if(!eps_eq(a[i], T(0))) k = i;
    }
    if(k == -1) return std::vector<T>();
    
    std::vector<std::pair<T, T>> next_bounds(d - 1);
    {
      int ni = 0;
      for(int i = 0;i < d; i++) {
        if(i == k) continue;
        next_bounds[ni++] = bounds[i];
      }
    }
    std::vector<std::vector<T>> bar_mat(next_mat.size() + 2, std::vector<T>(d));
    for(int mi = 0; mi < next_mat.size(); mi++) {
      auto ratio = next_mat[mi][k] / a[k];
      int ni = 0;
      for(int i = 0; i <= d; i++) {
        if(i == k) continue;
        bar_mat[mi][ni++] = next_mat[mi][i] - ratio * a[i];
      }
    }
    std::vector<T> bar_c(d - 1);
    {
      auto ratio = c[k] / a[k];
      int ni = 0;
      for(int i = 0; i < d; i++) {
        if(i == k) continue;
        bar_c[ni++] = c[i] - ratio * a[i];
      }
    }
    {
      int ni = 0;
      for(int i = 0; i < d; i++) {
        if(i == k) continue;
        bar_mat[next_mat.size()][ni] = - (T(1) / a[k]) * a[i];
        bar_mat[next_mat.size() + 1][ni++] = - (T(1) / a[k]) * a[i];
      }
      bar_mat[next_mat.size()][d - 1] = bounds[k].second;
      bar_mat[next_mat.size() + 1][d - 1] = bounds[k].first;
    }

    v = seidel_lp(rnd, d - 1, bar_c, bar_mat, next_bounds);
    if(v.empty()) {
      return v;
    }
    else {
      v.insert(std::begin(v) + k, T(0));
      T s = 0;
      for(int i = 0; i < d; i++) s += a[i] * v[i];
      v[k] = (a[d] - s) / a[k];
      return v;
    }
  }
}

#include <random>
#include <iostream>
#include <iomanip>

int main() {
  long double C, D;
  std::cin >> C >> D;
  auto r = std::mt19937(9982);
  std::vector<long double> c { 1.0, 2.0 };
  std::vector<std::vector<long double>> mat {
    { 3.0 / 4.0, 2.0 / 7.0, C },
    { 1.0 / 4.0, 5.0 / 7.0, D },
    { -1.0, 0.0, 0.0 },
    { 0.0, -1.0, 0.0 }
  };
  std::vector<std::pair<long double, long double>> bounds {
    { 0.0, 2000.0 },
    { 0.0, 2000.0 }
  };
  auto ans = seidel_lp(r, 2, c, mat, bounds);
  std::cout << std::fixed << std::setprecision(10) << ans[0] * 1000.0 + ans[1] * 2000.0 << std::endl;
}

Runtime fp

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

struct modint {
  i64 a;
  static i64& M() {
    static i64 mod_ = 2999;
    return mod_;
  }
  modint(const i64 x = 0) noexcept: a((x % M() + M()) % M()) {}
  i64 value() const noexcept { return a; }
  modint& operator=(const i64 r) {
    a = (r % M() + M()) % M();
    return *this;
  }
  modint& operator+=(const modint r) noexcept {
    a += r.a;
    if(a >= M()) a -= M();
    return *this;
  }
  modint& operator-=(const modint r) noexcept {
    a -= r.a;
    if(a < 0) a += M();
    return *this;
  }
  modint& operator*=(const modint r) noexcept {
    a = a * r.a % M();
    return *this;
  }
  modint& operator/=(modint r) noexcept {
    i64 ex = M() - 2;
    while(ex) {
      if(ex & 1) {
        *this *= r;
      }
      r *= r;
      ex >>= 1;
    }
    return *this;
  }

  modint operator+(const modint r) const {
    return modint(*this) += r;
  }
  modint operator-(const modint r) const {
    return modint(*this) -= r;
  }
  modint operator*(const modint r) const {
    return modint(*this) *= r;
  }
  modint operator/(const modint r) const {
    return modint(*this) /= r;
  }
};

F2[[x]]

Spec

gcdとか, modとかができます.

Code

#include <array>
#include <cstdint>
#include <cassert>


#include <iostream>
#include <bitset>

template<const std::size_t N>
struct binfps {
  using size_type = std::size_t;
  using bit_type = std::uint_fast64_t;

  const static size_type lg = 6;
  const static size_type lgmask = (bit_type(1) << lg) - 1;
  const static size_type _w_len = (N + (bit_type(1) << lg) - 1) >> lg;
  std::array<bit_type, _w_len> w;
  size_type len;

  binfps(): len(0) {
    for(size_type i = 0; i < _w_len; i++) {
      w[i] = 0;
    }
  }

  binfps(const std::bitset<N>& b) {
    for(size_type i = 0; i < _w_len; i++) {
      w[i] = 0;
    }
    for(size_type i = 0; i < N; i++) {
      if(b[i]) set(i);
    }
    recalc();
  }

  int size() const { return this->len; }
  void recalc() {
    len = 0;
    for(size_type i = w.size(); i --> 0; ) {
      if(w[i]) {
        for(size_type j = (1 << lg); j --> 0;) {
          if(w[i] >> j) {
            len = j + 1 + (i << lg);
            return;
          }
        }
      }
    }
  }

  void set(size_type i) { w[i >> lg] |= (bit_type(1) << (i & lgmask)); }
  void unset(size_type i) { w[i >> lg] &= ~(bit_type(1) << (i & lgmask)); }

  bool any() const {
    for(size_type i = 0; i < _w_len; i++) {
      if(w[i]) {
        return true;
      }
    }
    return false;
  }

  bool operator[](size_type i) const {
    return (w[i >> lg] >> (i & lgmask)) & 1;
  }

  binfps& operator^=(const binfps& b) {
    for(size_type i = w.size(); i --> 0;) {
      w[i] ^= b.w[i];
    }
    return *this;
  }
  binfps& operator&=(const binfps& b) {
    for(size_type i = w.size(); i --> 0;) {
      w[i] &= b.w[i];
    }
    return *this;
  }
  binfps& operator|=(const binfps& b) {
    for(size_type i = w.size(); i --> 0;) {
      w[i] &= b.w[i];
    }
    return *this;
  }


  binfps& operator<<=(size_type x) {
    std::array<bit_type, _w_len> next;
    for(size_type i = 0; i < _w_len; i++) {
      next[i] = 0;
    }
    size_type off = x >> lg;
    size_type m = x & lgmask;
    bit_type dwmask = ((bit_type)(1) << (64 - m)) - 1;
    if(m == 0) {
      dwmask = ~(bit_type)(0);
    }
    bit_type upmask = ~dwmask;
    // up
    for(size_type i = 0; i + off + 1 < _w_len; i++) {
      next[i + off + 1] |= (w[i] & upmask) >> (64 - m);
    }
    // down
    for(size_type i = 0; i + off < _w_len; i++) {
      next[i + off] |= (w[i] & dwmask) << m;
    }

    w = std::move(next);
    len = std::min(N, len + x);
    return (*this);
  }

  binfps& operator>>=(size_type x) {
    std::array<bit_type, _w_len> next;
    for(size_type i = 0; i < _w_len; i++) {
      next[i] = 0;
    }
    bit_type off = x >> lg;
    bit_type m = x & lgmask;
    bit_type dwmask = (bit_type(1) << m) - 1;
    if(m == 0) {
      dwmask = 0;
    }
    bit_type upmask = ~dwmask;
    // down
    for(size_type i = 0; i + off + 1 < _w_len; i++) {
      next[i] |= (w[i + off + 1] & dwmask) << (64 - m);
    }
    // up
    for(size_type i = 0; i + off < _w_len; i++) {
      next[i] |= (w[i + off] & upmask) >> m;
    }
    w = std::move(next);
    if(len < x) {
      len = 0;
    }
    else {
      len = len - x;
    }
    return (*this);
  }

  binfps operator^(const binfps& b) const { return binfps(*this) ^= b; }
  binfps operator&(const binfps& b) const { return binfps(*this) &= b; }
  binfps operator|(const binfps& b) const { return binfps(*this) |= b; }
  binfps operator<<(const size_type x) const { return binfps(*this) <<= x; }
  binfps operator>>(const size_type x) const { return binfps(*this) >>= x; }
  binfps operator~() {
    binfps a = *this;
    for(size_type i = w.size(); i --> 0;) {
      a.w[i] = ~w[i];
    }
    return a;
  }

  bool operator<(const binfps& b) const {
    bool OK = false;
    for(size_type i = _w_len; i --> 0; ) {
      if(w[i] != b.w[i]) {
        if(w[i] < b.w[i]) {
          OK = true;
        }
        break;
      }
    }
    return OK;
  }
  bool operator<=(const binfps& b) const {
    bool OK = true;
    for(size_type i = _w_len; i --> 0; ) {
      if(w[i] != b.w[i]) {
        if(w[i] > b.w[i]) {
          OK = false;
        }
        break;
      }
    }
    return OK;
  }

  static binfps mod(binfps a, const binfps& b) {
    assert(b.size() > 0);
    for(int i = (int)a.size() - (int)b.size() + 1; i --> 0;) {
      if(a[i + b.size() - 1]) {
        a ^= (b << i);
      }
    }
    a.recalc();
    return a;
  }

  static binfps div(binfps a, const binfps& b) {
    assert(b.size() > 0);
    binfps d;
    for(int i = (int)a.size() - (int)b.size() + 1; i --> 0;) {
      if(a[i + b.size() - 1]) {
        a ^= (b << i);

        binfps e;
        e.set(i);
        d ^= e;
      }
    }
    d.recalc();
    return d;
  }

  static binfps gcd(binfps a, binfps b) {
    while(b.any()) {
      auto m = mod(a, b);
      a = std::move(b);
      b = std::move(m);
    }
    return a;
  }

  void dump() const {
    for(size_type i = _w_len; i --> 0; ) {
      std::cerr << std::bitset<64>(w[i]) << "|" << std::endl;
    }
    std::cerr << std::endl;
  }
};


const int BN = 5000;
using bits = binfps<BN>;

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define all(x) x.begin(),x.end()

template<class T>
static inline std::vector<T> ndvec(size_t&& n, T val) noexcept {
  return std::vector<T>(n, std::forward<T>(val));
}

template<class... Tail>
static inline auto ndvec(size_t&& n, Tail&&... tail) noexcept {
  return std::vector<decltype(ndvec(std::forward<Tail>(tail)...))>(n, ndvec(std::forward<Tail>(tail)...));
}
 
int main() {
  i64 N;
  cin >> N;
  std::bitset<BN> binp;
  cin >> binp;
  bits X(binp);
  X.recalc();
 
  vector<bits> A(N);
  rep(i,0,N) {
    cin >> binp;
    A[i] = bits(binp);
    A[i].recalc();
  }
  auto G = A[0];
  for(i64 i = 1; i < N; i++) {
    G = bits::gcd(G, A[i]);
  }
  i64 ans = 0;
  const i64 MOD = 998244353;
  if(X.size() - G.size() + 1 >= 0) {
    vector<i64> Bs(X.size() - G.size() + 1, 1);
    for(i64 i = 1; i < X.size() - G.size() + 1; i++) {
      Bs[i] = (Bs[i - 1] * 2) % MOD;
    } 
    bits now;
    for(i64 i = X.size() - G.size() + 1; i --> 0;) {
      if(X[i + G.size() - 1]) {
        ans = (ans + Bs[i]) % MOD;
      }
      if(now[i + G.size() - 1] != X[i + G.size() - 1]) {
        now ^= (G << i);
      }
    }
    if(now <= X) ans = (ans + 1) % MOD;
  }
  cout << ans << endl;
}

factorial

Code

#include <vector>
#include <tuple>
using i64 = long long;

template<class T>
struct factorial {
  std::vector<T> fact;
  std::vector<T> finv;
  std::vector<T> inv;

  void build(int N) {
    fact.resize(N);
    finv.resize(N);
    inv.resize(N);
    fact[0] = T(1);
    for(int i = 1;i < N;i++) {
      fact[i] = fact[i - 1] * T(i);
    }
    finv[N - 1] = T(1) / fact[N - 1];
    for(int i = N - 1; i --> 0;) {
      finv[i] = finv[i + 1] * T(i + 1);
    }
    for(int i = 0;i < N;i++) {
      inv[i] = fact[i - 1] * finv[i];
    }
  }

  T binom(int n, int r) const {
    if(0 <= r && r <= n) return fact[n] * finv[n - r] * finv[r];
    else return T(0);
  }

  std::tuple<const std::vector<T>&, const std::vector<T>&, const std::vector<T>&> get() const {
    return std::tuple<const std::vector<T>&, const std::vector<T>&, const std::vector<T>&>(fact, finv, inv);
  }
};

osa_k

Code

#include <vector>
#include <numeric>
#include <set>

struct osa_k {
  using int_type = int;
  std::vector<int_type> min_fact;

  // O(NlogN)
  static std::vector<int_type> min_prime_factor(int n) {
    std::vector<int_type> res(n);
    std::iota(std::begin(res), std::end(res), 0);
    for(int i = 2; i * i < n; i++) {
      if(res[i] < i) continue;
      for(int j = i * i; j < n; j += i) {
        if(res[j] == j) res[j] = i;
      }
    }
    return res;
  }

  void build(int n) {
    min_fact = min_prime_factor(n);
  }

  // O(logN)
  std::vector<std::pair<int_type, int>> prime_factors(int n) const {
    std::vector<std::pair<int_type, int>> res;
    while(n > 1) {
      if(res.empty() || res.back().first != min_fact[n]) {
        res.push_back({ min_fact[n], 0 });
      }
      res.back().second++;
      n /= min_fact[n];
    }
    return res;
  }
  
  // The divisors are not sorted
  // O(logN + |divisors|)
  template<class F>
  void enumerate_divisors(int n, F f) const {
    std::vector<std::pair<int_type, int>> prime_facts = prime_factors(n);
    if(prime_facts.empty()) {
      f(1);
      return;
    }
    std::vector<int> cnt(prime_facts.size());
    std::vector<int> acc(prime_facts.size(), 1);
    while(true){
      f(acc.front());
      int i = 0;
      for(; i < prime_facts.size(); i++) {
        if((cnt[i]++) == prime_facts[i].second) {
          cnt[i] = 0;
        }
        else {
          acc[i] *= prime_facts[i].first;
          break;
        }
      }
      if(i == prime_facts.size()) {
        break;
      }
      while(i --> 0) {
        acc[i] = acc[i + 1];
      }
    }
  }
};

Adjucency Matrix Equation

Code

#include <vector>
#include <set>

struct adjucency_matrix_equation {
  int V;
  int E;
  std::vector<std::vector<std::pair<int, int>>> G;
  adjucency_matrix_equation(int n): V(n), E(0), G(n) {}

  void add_edge(int v, int u) {
    G[v].emplace_back(u + V, E);
    G[u].emplace_back(v, E);
    E++;
  }
  
  template<class T>
  std::vector<T> solve(const std::vector<T>& c) const {
    std::vector<int> vis(V, 0);
    std::vector<T> ans(E, 0);

    auto dfs = [&](auto f, int v) -> T {
      vis[v] = 1;
      T r = c[v];
      for(auto e: G[v]) {
        int t = e.first;
        int ei = e.second;
        if(vis[t < V ? t : t - V]) continue;
        T y;
        if(t < V) ans[ei] = (y = f(f, t));
        else      ans[ei] = T(0) - (y = f(f, t - V));
        r = r + y;
      }
      return r;
    };

    for(int v = 0; v < V; v++) {
      if(vis[v]) continue;
      T y = dfs(dfs, v);
      if(y != T(0)) return std::vector<T>();
    }
    return ans;
  }
};

Berlekamp-Massey

線形漸化式\( a_i = c_0 * a_(i - 1) + c_1 * a_(i - 2) + ... \)があって、数列\(a\)がわかっているときに、漸化式の係数\(c\)を\(O(n^2)\)で求める.

Code

#include <vector>

template<class F>
std::vector<F> berlekamp_massey(const std::vector<F>& s) {
  int n = s.size();
  std::vector<F> b { F(1) };
  std::vector<F> c { F(1) };
  F y(1);
  int shift = 0;
  for(int len = 0; len < n; len++) {
    shift++;
    F x(0);
    for(int i = 0; i < c.size(); i++) {
      x += c[i] * s[len - i];
    }
    if(x == F(0)) { continue; }
    std::vector<F> old_c = c;
    F freq = x / y; c.resize(std::max(c.size(), b.size() + shift), F(0));
    for(int i = 0; i < b.size(); i++) {
      c[i + shift] -= freq * b[i];
    }
    if(old_c.size() < c.size()) {
      b = std::move(old_c);
      y = x;
      shift = 0;
    }
  }
  std::vector<F> ans(c.size() - 1);
  for(int i = 1; i < c.size(); i++) {
    ans[i - 1] = -c[i];
  }
  return ans;
}

Fast Kitamasa

線形漸化式\(a_i = c_1 * a_{i - 1} + \cdots + c_d * a_{i - d}\)があるとき、\(c_0 = -1\)としてfast_kitamasaにかけると、 \(a_n = b_0 * a_{d - 1} + \cdots + b_{d - 1} * a_0\)となる\(b\)を\(O(d \log d \log n)\)で求める

#include "formal_power_series.hpp"
#include <vector>
using i64 = long long;

template<class F, class FM>
std::vector<F> fast_kitamasa(std::vector<F> c, i64 n) {
  using fps = FPS<FM>;
  i64 k = c.size() - 1;
  fps cf(std::move(c));
  fps ic = cf.inv(k - 1);
  
  i64 c_len = bound_pow2(k - 1 + cf.size() - 1);
  i64 ic_len = bound_pow2(k - 1 + ic.size() - 1);
  auto dc = cf.resized(c_len).dft(c_len);
  auto dic = std::move(ic).dft(ic_len);

  i64 b_len = bound_pow2(k);
  fps b(std::vector<F>(b_len, F(0)));
  b[k - 1] = F(1);
  i64 m = bound_pow2(n);
  while(m) {
    auto bt = std::move(b).dft(b_len * 2);
    bt *= bt;
    auto beta = std::move(bt).idft();
    
    auto dbeta = beta.resized(k - 1).dft(ic_len);
    auto q = std::move(dbeta *= dic).idft(c_len);
    for(int i = k - 1; i < c_len; i++) q[i] = F(0);
    fps cfq = std::move(std::move(q).dft(c_len) *= dc).idft(k - 1 + cf.size() - 1);
    beta -= cfq;

    b = fps(std::vector<F>(b_len * 2));
    for(int i = k - 1; i < 2 * k - 1; i++) {
      b[i - (k - 1)] = beta[i];
    }
    if(m & n) {
      F freq = b[0];
      for(int i = 0; i < k - 1; i++) {
        b[i] = b[i + 1] + freq * cf[i + 1];
      }
      b[k - 1] = freq * cf[k];
    }
    m >>= 1;
  }
  b.resize(k);
  return std::move(b.coef);
}

Black Box Linear Algebra

ベクトルと行列の掛け算に\(T(n)\)時間かかるとします.

最小多項式

\(O(n^2 + n T(n))\)

#include "../berlekamp_massey.hpp"
#include <vector>

template<class F>
std::vector<F> find_minimal_polynomial(const std::vector<F>& a) {
  std::vector<F> c = berlekamp_massey(a);
  c.insert(c.begin(), -F(1));
  return c;
}

template<class F, class NonZeroRandGen>
std::vector<F> find_minimal_polynomial_from_vector(int n, const std::vector<std::vector<F>>& a, NonZeroRandGen rnd) {
  std::vector<F> u(n);
  for(int i = 0; i < n; i++) u[i] = rnd();
  std::vector<F> b(a.size(), F(0));
  for(int i = 0; i < a.size(); i++) {
    for(int j = 0; j < n; j++) {
      b[i] += a[i][j] * u[j];
    }
  }
  return find_minimal_polynomial(b);
}

template<class F, class NonZeroRandGen>
std::vector<F> find_minimal_polynomial_from_dense_matrix_pow_b(const std::vector<std::vector<F>>& a, std::vector<F> b, NonZeroRandGen rnd) {
  int n = a.size();
  std::vector<F> bf;

  std::vector<F> u(n);
  for(int i = 0; i < n; i++) u[i] = rnd();

  std::vector<F> c(n * 2);
  for(int i = 0; i < 2 * n; i++) {
    for(int j = 0; j < n; j++) {
      c[i] += b[j] * u[j];
    }
    if(i + 1 < 2 * n) {
      bf = b;
      for(int j = 0; j < n; j++) {
        b[j] = F(0);
        for(int k = 0; k < n; k++) {
          b[j] += a[j][k] * bf[k];
        }
      }
    }
  }
  return find_minimal_polynomial(c);
}

// fast for dense matrix
template<class F, class NonZeroRandGen>
std::vector<F> find_minimal_polynomial_from_dense_matrix_pow(const std::vector<std::vector<F>>& a, NonZeroRandGen rnd) {
  int n = a.size();
  std::vector<F> b(n);
  for(int i = 0; i < n; i++) b[i] = rnd();
  return find_minimal_polynomial_from_dense_matrix_pow_b(a, std::move(b), rnd);
}

#include <tuple>

template<class F, class NonZeroRandGen>
std::vector<F> find_minimal_polynomial_from_sparse_matrix_pow_b(const std::vector<std::tuple<int, int, F>>& a, std::vector<F> b, int n, NonZeroRandGen rnd) {
  std::vector<F> bf;

  std::vector<F> u(n);
  for(int i = 0; i < n; i++) u[i] = rnd();

  std::vector<F> c(n * 2);
  for(int i = 0; i < 2 * n; i++) {
    for(int j = 0; j < n; j++) {
      c[i] += b[j] * u[j];
    }
    if(i + 1 < 2 * n) {
      bf = b;
      for(int j = 0; j < n; j++) {
        b[j] = F(0);
      }
      for(auto& [j, k, v]: a) {
        b[j] += v * bf[k];
      }
    }
  }
  return find_minimal_polynomial(c);
}

template<class F, class NonZeroRandGen>
std::vector<F> find_minimal_polynomial_from_sparse_matrix_pow(const std::vector<std::tuple<int, int, F>>& a, int n, NonZeroRandGen rnd) {
  std::vector<F> b(n);
  for(int i = 0; i < n; i++) b[i] = rnd();
  return find_minimal_polynomial_from_sparse_matrix_pow_b(a, std::move(b), n, rnd);
}

行列式

\(O(n^2 + n T(n))\)

#include "minimal_polynomial.hpp"
#include <vector>
#include <tuple>

template<class F, class NonZeroRandGen>
F fast_determinant_dense(std::vector<std::vector<F>> a, NonZeroRandGen rng) {
  int n = a.size();
  std::vector<F> d(n);
  F d_det(1);
  for(int i = 0; i < n; i++) {
    d[i] = rng();
    d_det *= d[i];
  }
  for(int i = 0; i < n; i++) {
    for(int j = 0; j < n; j++) {
      a[i][j] = a[i][j] * d[j];
    }
  }
  auto c = find_minimal_polynomial_from_dense_matrix_pow2(a, rng);
  F det = c.back() / c.front();
  det = n & 1 ? -det : det;
  return det / d_det;
}

template<class F, class NonZeroRandGen>
F fast_determinant_sparse(std::vector<std::tuple<int, int, F>> a, int n, NonZeroRandGen rng) {
  std::vector<F> d(n);
  F d_det(1);
  for(int i = 0; i < n; i++) {
    d[i] = rng();
    d_det *= d[i];
  }
  for(auto& [i, j, v]: a) {
    v *= d[j];
  }
  auto c = find_minimal_polynomial_from_sparse_matrix_pow2(a, n, rng);
  F det = c.back() / c.front();
  det = n & 1 ? -det : det;
  return det / d_det;
}

行列累乗

\(A^k b\)を求める. fast_kitamasaを用いて\(O(N^3 + N \log N \log k)\)

#include "../fast_kitamasa.hpp"
#include <vector>
#include <tuple>
template<class F, class FM, class NonZeroRandGen>
std::vector<F> bbla_dense_matrix_pow(const std::vector<std::vector<F>>& a, std::vector<F> b, long long r, NonZeroRandGen rnd) {
  int n = a.size();
  auto c = find_minimal_polynomial_from_dense_matrix_pow_b(a, b, rnd);
  auto d = fast_kitamasa<F, FM>(std::move(c), r);
  std::vector<F> ans(n);
  std::vector<F> bf;
  for(int i = 0; i < d.size(); i++) {
    for(int j = 0; j < n; j++) {
      ans[j] += d[d.size() - 1 - i] * b[j];
    }
    if(i + 1 < d.size()) {
      bf = b;
      for(int j = 0; j < n; j++) {
        b[j] = F(0);
        for(int k = 0; k < n; k++) {
          b[j] += a[j][k] * bf[k];
        }
      }
    }
  }
  return ans;
}

template<class F, class FM, class NonZeroRandGen>
std::vector<F> bbla_sparse_matrix_pow(const std::vector<std::tuple<int, int, F>>& a, std::vector<F> b, long long r, NonZeroRandGen rnd) {
  int n = b.size();
  auto c = find_minimal_polynomial_from_sparse_matrix_pow_b(a, b, n, rnd);
  auto d = fast_kitamasa<F, FM>(std::move(c), r);
  std::vector<F> ans(n);
  std::vector<F> bf;
  for(int i = 0; i < d.size(); i++) {
    for(int j = 0; j < n; j++) {
      ans[j] += d[d.size() - 1 - i] * b[j];
    }
    if(i + 1 < d.size()) {
      bf = b;
      for(int j = 0; j < n; j++) {
        b[j] = F(0);
      }
      for(auto& [j, k, v]: a) {
        b[j] += v * bf[k];
      }
    }
  }
  return ans;
}

Convolution

畳み込み 参考 - 約数集合でのゼータ変換・メビウス変換的なやつと畳み込み

集合の畳み込みについて

\(A \subseteq B\) のとき, \( B \)は\( A \)の上位集合という. また\( A \)は\( B \)の下位集合という.

上位集合について和を求める変換して互いをかけ合わせて逆変換をすると, 積集合(And)の個数が求められる.
下位集合について和を求める変換して互いをかけ合わせて逆変換をすると, 和集合(Or)の個数が求められる.

例) multiple_transform(iについて, iを約数に持つja[j]の総和を求める --> 上位集合)をして互いをかけ合わせて逆変換すると, 積集合(gcdについての畳み込み)がの個数が求められる.

Transforms

Multiple Transform

約数幇助と呼ばれる. 参考 - 高速ゼータ変換の約数版

Spec

iについて, iを約数に持つj(上位集合)のa[j]の総和を求める. \( O(N \log{\log N}) \).
inverse multiple transformと共に使うとgcdに関する畳み込みができる. こんな感じ

\[ h(z) = \sum_{\gcd(x, y) = z} {f(x) * g(y)} \]

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

template <class T>
void multiple_transform(vector<T> &a) {
	int n = a.size();
	vector<bool> sieve(n, true);
	for (int p = 2; p < n; ++p) {
		if (sieve[p]) {
			for (int k = (n - 1) / p; k != 0; --k) {
				sieve[k * p] = false;
				a[k] += a[k * p];
			}
		}
	}
	for (int i = 0; ++i != n;) {
		a[i] += a[0];
	}
}

template <class T>
void inverse_multiple_transform(vector<T> &a) {
	int n = a.size();
	vector<bool> sieve(n, true);
	for (int i = 0; ++i != n;) {
		a[i] -= a[0];
	}
	for (int p = 2; p < n; ++p) {
		if (sieve[p]) {
			for (int k = 1; k * p < n; ++k) {
				sieve[k * p] = false;
				a[k] -= a[k * p];
			}
		}
	}
}

Divisor Transform

約数幇助と呼ばれる. 参考 - 高速ゼータ変換の約数版

Spec

iについて, iの約数であるj(下位集合)についてa[j]の総和を求める. \( O(N \log{\log N}) \).
inverse divisor transformと共に使うとlcmに関する畳み込みができる. こんな感じ

\[ h(z) = \sum_{ \operatorname{lcm} (x, y) = z } { f(x) * g(y) } \]

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

template <class T>
void divisor_transform(vector<T> &a) {
	int n = a.size();
	vector<bool> sieve(n, true);
	for (int p = 2; p < n; ++p) {
		if (sieve[p]) {
			for (int k = 1; k * p < n; ++k) {
				sieve[k * p] = false;
				a[k * p] += a[k];
			}
		}
	}
	for (int i = 0; ++i != n;) {
		a[i] += a[0];
	}
}

template <class T>
void inverse_divisor_transform(vector<T> &a) {
	int n = a.size();
	vector<bool> sieve(n, true);
	for (int i = 0; ++i != n;) {
		a[i] -= a[0];
	}
	for (int p = 2; p < n; ++p) {
		if (sieve[p]) {
			for (int k = (n - 1) / p; k != 0; --k) {
				sieve[k * p] = false;
				a[k * p] -= a[k];
			}
		}
	}
}

Fast Fourier Transform

Spec

離散フーリエ変換(discrete Fourier transformation)を\( O(n \log n) \)で行う.

誤差厳しい, 整数ならNTT使おう(誤差をなくすようにFFTするのもあるらしい)

Code

#include <vector>
#include <iostream>
#include <complex>
using namespace std;
using i64 = long long;

const double pi = std::acos(-1);

vector<complex<double>> fast_fourier_transform(vector<complex<double>> a) {
  i64 n = a.size();
  for(i64 s = n >> 1; s >= 1; s >>= 1) {
    complex<double> zeta = std::polar(1.0, 2 * pi / (double)(s << 1));
    for(i64 i = 0;i < n;i += (s << 1)) {
      complex<double> zi = 1.0;
      for(i64 j = 0;j < s;j++) {
        complex<double> t = a[i + j] - a[s + i + j];
        a[i + j] = a[i + j] + a[s + i + j];
        a[s + i + j] = t * zi;
        zi = zi * zeta;
      }
    }
  }
  return a;
}

vector<complex<double>> inverse_fast_fourier_transform(vector<complex<double>> a) {
  i64 n = a.size();
  for(i64 s = 1; s < n; s <<= 1) {
    complex<double> zeta = std::polar(1.0, -1 * 2 * pi / (double)(s << 1));
    for(i64 i = 0; i < n; i += (s << 1)) {
      complex<double> zi = 1;
      for(i64 j = 0;j < s;j++) {
        complex<double> t = a[s + i + j] * zi;
        a[s + i + j] = a[i + j] - t;
        a[i + j] = a[i + j] + t;
        zi = zi * zeta;
      }
    }
  }
  i64 inv_n = 1 / (double)n;
  for(int i = 0;i < n;i++) a[i] *= inv_n;
  return a;
}

Numeric Theoretic Transform

Spec

素数\( m = 2^k + 1, 2^k >= n\)として\(F_m \)剰余環上での離散フーリエ変換(discrete Fourier transformation)を\( O(n \log n) \)で行う
NTT_PRIMESるまライブラリからお借りしています

ほとんど単体でNTTを使うことはなさそう(FPSで使うね)

Code

#include "../modint.hpp"
#include <vector>
using namespace std;
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<const i64 mod, const i64 primitive>
vector<modint<mod>> number_theoretic_transform(vector<modint<mod>> a) {
  i64 n = a.size();
  for(i64 s = n >> 1; s >= 1; s >>= 1) {
    modint<mod> zeta = modint<mod>(primitive).pow((mod - 1) / (s << 1));
    for(i64 i = 0; i < n; i += (s << 1)) {
      modint<mod> zi = 1;
      for(i64 j = 0;j < s;j++) {
        modint<mod> t = a[i + j] - a[s + i + j];
        a[i + j] += a[s + i + j];
        a[s + i + j] = t * zi;
        zi = zi * zeta;
      }
    }
  }
  return a;
}

template<const i64 mod, const i64 primitive>
vector<modint<mod>> inverse_number_theoretic_transform(vector<modint<mod>> a) {
  i64 n = a.size();
  for(i64 s = 1; s < n; s <<= 1) {
    modint<mod> zeta = modint<mod>(primitive).pow((mod - 1) / (s << 1)).pow(mod - 2);
    for(i64 i = 0; i < n; i += (s << 1)) {
      modint<mod> zi = 1;
      for(i64 j = 0;j < s;j++) {
        modint<mod> t = a[s + i + j] * zi;
        a[s + i + j] = a[i + j] - t;
        a[i + j] = a[i + j] + t;
        zi = zi * zeta;
      }
    }
  }
  auto inv_n = modint<mod>(n).pow(mod - 2);
  for(int i = 0;i < n;i++) a[i] *= inv_n;
  return a;
}

4基底NTT

#include "../modint.hpp"
#include <vector>
using namespace std;
using i64 = long long;


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<const i64 mod, const i64 primitive>
vector<modint<mod>> number_theoretic_transform4(vector<modint<mod>> a) {
  i64 n = a.size();
  vector<modint<mod>> b(a.size());
  auto unit_i = modint<mod>(primitive).pow((mod - 1) / 4);
  for(i64 s = 1, m = n; s < n; s <<= 1, m >>= 1) {
    if(m == 2) {
      for(i64 j = 0;j < s;j++) {
        auto x = a[j + 0];
        auto y = a[j + s];
        b[j + 0] = x + y;
        b[j + s] = x - y;
      }
    }
    else {
      modint<mod> zi1 = 1;
      modint<mod> zi2 = 1;
      modint<mod> zi3 = 1;
      i64 m1 = m >> 2;
      i64 m2 = m >> 1;
      i64 m3 = m1 | m2;
      modint<mod> zeta = modint<mod>(primitive).pow((mod - 1) / m);
      for(i64 i = 0;i < m1;i++) {
        for(i64 j = 0;j < s;j++) {
          auto w = a[j + s * (i + 0)];
          auto x = a[j + s * (i + m1)];
          auto y = a[j + s * (i + m2)];
          auto z = a[j + s * (i + m3)];
          auto wy1 = w + y;
          auto wy2 = w - y;
          auto xz1 = x + z;
          auto xz2 = (x - z) * unit_i;
          b[j + s * (4 * i + 0)] =  wy1 + xz1;
          b[j + s * (4 * i + 1)] = (wy2 + xz2) * zi1;
          b[j + s * (4 * i + 2)] = (wy1 - xz1) * zi2;
          b[j + s * (4 * i + 3)] = (wy2 - xz2) * zi3;
        }
        zi1 = zi1 * zeta;
        zi2 = zi1 * zi1;
        zi3 = zi1 * zi2;
      }
      s <<= 1;
      m >>= 1;
    }
    swap(a, b);
  }
  return a;
}

template<const i64 mod, const i64 primitive>
vector<modint<mod>> inverse_number_theoretic_transform4(vector<modint<mod>> a) {
  i64 n = a.size();
  vector<modint<mod>> b(a.size());
  auto unit_i = modint<mod>(primitive).pow((mod - 1) / 4).inv();
  i64 s = n;
  i64 m = 1;
  if(__builtin_ctzll(n) & 1) {
    s >>= 1;
    m <<= 1;
    for(i64 j = 0;j < s;j++) {
      auto x = a[j + 0];
      auto y = a[j + s];
      b[j + 0] = x + y;
      b[j + s] = x - y;
    }
    swap(a, b);
  }
  for(; s >>= 2, m <<= 2, s >= 1;) {
    {
      modint<mod> zi1 = 1;
      modint<mod> zi2 = 1;
      modint<mod> zi3 = 1;
      i64 m1 = m >> 2;
      i64 m2 = m >> 1;
      i64 m3 = m1 | m2;
      modint<mod> zeta = modint<mod>(primitive).pow((mod - 1) / m).inv();
      for(i64 i = 0;i < m1;i++) {
        for(i64 j = 0;j < s;j++) {
          auto w = a[j + s * (4 * i + 0)];
          auto x = a[j + s * (4 * i + 1)] * zi1;
          auto y = a[j + s * (4 * i + 2)] * zi2;
          auto z = a[j + s * (4 * i + 3)] * zi3;
          auto wy1 = w + y;
          auto wy2 = x + z;
          auto xz1 = w - y;
          auto xz2 = (x - z) * unit_i;
          b[j + s * (i + 0)]  = wy1 + wy2;
          b[j + s * (i + m1)] = xz1 + xz2;
          b[j + s * (i + m2)] = wy1 - wy2;
          b[j + s * (i + m3)] = xz1 - xz2;
        }
        zi1 = zi1 * zeta;
        zi2 = zi1 * zi1;
        zi3 = zi1 * zi2;
      }
    }
    swap(a, b);
  }
  auto inv_n = modint<mod>(n).pow(mod - 2);
  for(int i = 0;i < n;i++) a[i] *= inv_n;
  return a;
}

Zeta Mobius Transform

String

文字列の頭良い感じの線形アルゴリズムたち

2^31 Rolling Hash

Code

#include <vector>
using u64 = unsigned long long;

struct hash_bases {
  static const u64 MOD = (1ull << 61) - 1;
  const int N;
  std::vector<std::vector<u64>> bases;
  
  inline static u64 mod(u64 x) {
    x = (x >> 61) + (x & MOD);
    if(x >= MOD) {
      x -= MOD;
    }
    return x;
  }

  inline static u64 mul(u64 a, u64 b) {
    u64 ac = a >> 31;
        a  = a & ((1ull << 31) - 1);
    u64 bc = b >> 31;
        b  = b & ((1ull << 31) - 1);
    u64 x = ac * b + bc * a;;
    x = ac * bc * 2 + (x >> 30) + ((x & ((1ull << 30) - 1)) << 31) + a * b;
    return mod(x);
  }
  
  hash_bases(std::vector<u64> bs, int M) : N(bs.size()), bases(M, std::vector<u64>(N, 1)) {
    for(int i = 1; i < M; i++) {
      for(int j = 0;j < N; j++){
        bases[i][j] = mul(bases[i - 1][j], bs[j]);
      }
    }
  }

  u64 operator()(int i, int r) {
    return bases[r][i];
  }
} bases(std::vector<u64> { 9973, 10007 }, 202020);

struct rhash {
  static const u64 MOD = hash_bases::MOD;
  std::vector<u64> h;
  u64 len;

  rhash() : h(bases.N), len(0) {}
  rhash(u64 x): h(bases.N), len(1) {
    x = hash_bases::mod(x);
    for(int i = 0; i < bases.N; i++) {
      h[i] = x;
    }
  }

  rhash next(u64 r) const {
    rhash ans;
    for(int i = 0;i < bases.N; i++) { 
      ans.h[i] = hash_bases::mul(h[i], bases(i, r));
    }
    ans.len = len + r;
    return ans;
  }

  rhash& operator+=(const rhash& r) {
    for(int i = 0;i < bases.N; i++) {
      h[i] += r.h[i];
      if(h[i] >= MOD) h[i] -= MOD;
    }
    len = std::max(len, r.len);
    return (*this);
  }

  rhash& operator-=(const rhash& r) {
    for(int i = 0;i < bases.N; i++) {
      if(h[i] < r.h[i]) {
        h[i] = h[i] + MOD - r.h[i];
      }
      else {
        h[i] = h[i] - r.h[i];
      }
    }
    return (*this);
  }

  rhash operator+(const rhash& r) const { return rhash(*this) += r; }
  rhash operator-(const rhash& r) const { return rhash(*this) -= r; }
  bool operator==(const rhash& r) const {
    bool OK = true;
    for(int i = 0;i < bases.N; i++){
      OK &= h[i] == r.h[i];
    }
    return OK;
  }
};

Manacher

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

/*
 * 文字 i を中心とする最長の回文の半径 = (全長 + 1) / 2
 */

vector<i64> manacher(string S) {
  vector<i64> R(S.size());
  int i = 0, j = 0;
  while (i < S.size()) {
    while (i-j >= 0 && i+j < S.size() && S[i-j] == S[i+j]) ++j;
    R[i] = j;
    int k = 1;
    while (i-k >= 0 && i+k < S.size() && k+R[i-k] < j) R[i+k] = R[i-k], ++k;
    i += k; j -= k;
  }
  return R;
}

MP (Morris Pratt)

文字列の頭良い感じの線形アルゴリズムたち

Spec

文字列S[0, i-1]のprefixとsuffixが最大何文字一致しているかを \( O(|S|) \)で求める.

Example

aabaabaaa
_010123452

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

/*
 * 文字列S[0, i-1]のprefixとsuffixが最大何文字一致しているか
 */

vector<i64> mp(string S) {
  vector<i64> A(S.size() + 1);
  A[0] = -1;
  int j = -1;
  for (int i = 0; i < S.size(); i++) {
    while (j >= 0 && S[i] != S[j]) j = A[j];
    j++;
    A[i+1] = j;
  }
  return A;
}

Suffix Array

Code

#include <vector>
#include <iostream>

template<class T>
const std::vector<int>& sa_is(std::vector<T> s, int k) {
  int N = s.size();
  static std::vector<int> sa;
  static std::vector<int> cnt;
  sa.resize(N + 1);

  if(N == 0) return sa;

  for(auto& c: s) c++;
  s.push_back(0);
  k++;

  std::vector<bool> iss(N + 1);
  std::vector<int> lms;
  std::vector<int> is_lms(N + 1, -1);
  std::vector<int> bin(k + 1);

  iss[N] = true;
  bin[1]++;
  for(int i = N; i --> 0; ) {
    if(s[i] == s[i + 1])
      iss[i] = iss[i + 1];
    else
      iss[i] = s[i] < s[i + 1];
    if(!iss[i] && iss[i + 1]) {
      is_lms[i + 1] = lms.size();
      lms.push_back(i + 1);
    }
    bin[s[i] + 1]++;
  }

  for(int i = 1;i <= k;i++)
    bin[i] += bin[i - 1];

  auto induce = [&](const std::vector<int>& lms) {
    sa.assign(N + 1, -1);
    cnt.assign(k, 0);

    for(int i = 0;i < lms.size();i++) {
      int x = lms[i];
      sa[bin[s[x] + 1] - 1 - cnt[s[x]]] = x;
      cnt[s[x]]++;
    }

    cnt.assign(k, 0);
    for(int i = 0;i <= N;i++) {
      int x = sa[i] - 1;
      if(x >= 0 && !iss[x]) {
        sa[bin[s[x]] + cnt[s[x]]] = x;
        cnt[s[x]]++;
      }
    }

    cnt.assign(k, 0);
    for(int i = N + 1;i --> 0;) {
      int x = sa[i] - 1;
      if(x >= 0 && iss[x]) {
        sa[bin[s[x] + 1] - 1 - cnt[s[x]]] = x;
        cnt[s[x]]++;
      }
    }
  };

  induce(lms);


  if(lms.size() >= 2) {
    int M = lms.size();
    int li = 0;
    std::vector<int> rec_lms(M);
    for(auto x: sa) {
      if(is_lms[x] != -1) rec_lms[li++] = x;
    }
    int rec_n = 1;
    std::vector<int> rec_s(M);
    rec_s[M - 1 - is_lms[rec_lms[1]]] = 1;
    for(int i = 2;i < M;i++) {
      int xl = rec_lms[i];
      int yl = rec_lms[i - 1];
      int xr = lms[is_lms[xl] - 1];
      int yr = lms[is_lms[yl] - 1];
      if(xr - xl != yr - yl)
        rec_n++;
      else {
        while(xl <= xr) {
          if(s[xl] != s[yl]) {
            rec_n++;
            break;
          }
          xl++;
          yl++;
        }
      }
      rec_s[M - 1 - is_lms[rec_lms[i]]] = rec_n;
    }


    sa_is(std::move(rec_s), rec_n + 1);
    li = M;
    for(int i = 1;i < M + 1;i++) {
      rec_lms[--li] = lms[M - 1 - sa[i]];
    }
    induce(rec_lms);
  }

  return sa;
}

Z-algorithm

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

vector<i64> z_algorithm(string S) {
  vector<i64> A(S.size());
  A[0] = S.size();
  int i = 1, j = 0;
  while (i < S.size()) {
    while (i+j < S.size() && S[j] == S[i+j]) ++j;
    A[i] = j;
    if (j == 0) { ++i; continue;}
    int k = 1;
    while (i+k < S.size() && k+A[k] < j) A[i+k] = A[k], ++k;
    i += k; j -= k;
  }
  return A;
}

Burrows Wheeler

#include <vector>
#include <iostream>

template<class T>
const std::vector<int>& sa_is(std::vector<T> s, int k) {
  int N = s.size();
  static std::vector<int> sa;
  static std::vector<int> cnt;
  sa.resize(N + 1);

  if(N == 0) return sa;

  for(auto& c: s) c++;
  s.push_back(0);
  k++;

  std::vector<bool> iss(N + 1);
  std::vector<int> lms;
  std::vector<int> is_lms(N + 1, -1);
  std::vector<int> bin(k + 1);

  iss[N] = true;
  bin[1]++;
  for(int i = N; i --> 0; ) {
    if(s[i] == s[i + 1])
      iss[i] = iss[i + 1];
    else
      iss[i] = s[i] < s[i + 1];
    if(!iss[i] && iss[i + 1]) {
      is_lms[i + 1] = lms.size();
      lms.push_back(i + 1);
    }
    bin[s[i] + 1]++;
  }

  for(int i = 1;i <= k;i++)
    bin[i] += bin[i - 1];

  auto induce = [&](const std::vector<int>& lms) {
    sa.assign(N + 1, -1);
    cnt.assign(k, 0);

    for(int i = 0;i < lms.size();i++) {
      int x = lms[i];
      sa[bin[s[x] + 1] - 1 - cnt[s[x]]] = x;
      cnt[s[x]]++;
    }

    cnt.assign(k, 0);
    for(int i = 0;i <= N;i++) {
      int x = sa[i] - 1;
      if(x >= 0 && !iss[x]) {
        sa[bin[s[x]] + cnt[s[x]]] = x;
        cnt[s[x]]++;
      }
    }

    cnt.assign(k, 0);
    for(int i = N + 1;i --> 0;) {
      int x = sa[i] - 1;
      if(x >= 0 && iss[x]) {
        sa[bin[s[x] + 1] - 1 - cnt[s[x]]] = x;
        cnt[s[x]]++;
      }
    }
  };

  induce(lms);


  if(lms.size() >= 2) {
    int M = lms.size();
    int li = 0;
    std::vector<int> rec_lms(M);
    for(auto x: sa) {
      if(is_lms[x] != -1) rec_lms[li++] = x;
    }
    int rec_n = 1;
    std::vector<int> rec_s(M);
    rec_s[M - 1 - is_lms[rec_lms[1]]] = 1;
    for(int i = 2;i < M;i++) {
      int xl = rec_lms[i];
      int yl = rec_lms[i - 1];
      int xr = lms[is_lms[xl] - 1];
      int yr = lms[is_lms[yl] - 1];
      if(xr - xl != yr - yl)
        rec_n++;
      else {
        while(xl <= xr) {
          if(s[xl] != s[yl]) {
            rec_n++;
            break;
          }
          xl++;
          yl++;
        }
      }
      rec_s[M - 1 - is_lms[rec_lms[i]]] = rec_n;
    }


    sa_is(std::move(rec_s), rec_n + 1);
    li = M;
    for(int i = 1;i < M + 1;i++) {
      rec_lms[--li] = lms[M - 1 - sa[i]];
    }
    induce(rec_lms);
  }

  return sa;
}

#include <cstdint>
#include <set>
#include <vector>
#include <iostream>

using i64 = long long;

class bitvector {
  using bit_type = std::uint_least64_t;
  using size_type = std::size_t;
  static constexpr size_type wordsize = 64;
  
  std::vector<bit_type> bit;
  std::vector<size_type> sum;
  
public:

  bitvector() : bit(), sum() {} 
  bitvector(const size_type size)
    : bit(size / wordsize + 1, 0), sum(size / wordsize + 1, 0) {}

  void set(const size_type i) {
    bit[i / wordsize] |= static_cast<bit_type>(1) << (i % wordsize);
  }
  void build() {
    for (size_type i = 1; i < bit.size(); i++) {
      sum[i] = sum[i - 1] + __builtin_popcountll(bit[i - 1]);
    }
  }

  size_type at(const size_type i) const {
    return bit[i / wordsize] >> (i % wordsize);
  }

  // count of ones in [0, i)
  size_type rank(const size_type i) const {
    return sum[i / wordsize]
      + __builtin_popcountll(bit[i / wordsize] & (static_cast<bit_type>(1) << (i % wordsize)) - 1);
  }

  // count of ones in [0, i)
  size_type rank(const size_type i, const size_type b) const {
    size_type ans = sum[i / wordsize]
      + __builtin_popcountll(bit[i / wordsize] & (static_cast<bit_type>(1) << (i % wordsize)) - 1);
    if(b) return ans;
    else return i - ans;
  }
};

template<class T>
class wavelet_matrix {
  using Integer = T;
  using integer_type = Integer;
  using size_type = std::size_t;


  size_type depth;
  size_type len;
  std::vector<bitvector> mat;
  std::vector<size_type> spl;

public:

  wavelet_matrix(): depth(0), len(0) {}
  wavelet_matrix(const std::vector<integer_type>& arr, size_type de)
    : depth(de),
      len(arr.size()),
      mat(std::vector<bitvector>(depth, bitvector(arr.size()))),
      spl(std::vector<size_type>(depth, 0)) {
        std::vector<size_type> idx(len);
        std::vector<size_type> left(len), right(len);
        for(size_type i = 0;i < len;i++) idx[i] = i;
        for(size_type d = depth; d-- > 0;) {
          size_type l = 0, r = 0;
          
          for(size_type i = 0; i < len; i++) {
            size_type k = (arr[idx[i]] >> d) & 1;
            if(k) right[r++] = idx[i], mat[d].set(i);
            else left[l++] = idx[i];
          }
          mat[d].build();
          spl[d] = l;
          swap(idx, left);
          for(size_type i = 0; i < r; i++) idx[i + l] = right[i];
        }
      }

  integer_type at(size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type k = mat[d].at(i);
      x |= (static_cast<integer_type>(k) << d);
      i = mat[d].rank(i, k) + spl[d] * k;
    }
    return x;
  }

  // counting elements that equal to x in range [left, right)
  size_type rank_x(size_type left, size_type right, integer_type x) const {
    for(size_type d = depth; d-- > 0;) {
      size_type k = ((x >> d) & 1);
      if(k) {
        left = mat[d].rank(left, k) + spl[d];
        right = mat[d].rank(right, k) + spl[d];
      }
      else {
        left = mat[d].rank(left, k);
        right = mat[d].rank(right, k);
      }
    }
    return right - left;
  }

  // sorted(arr[left..right])[i]
  integer_type quantile(size_type left, size_type right, size_type i) const {
    integer_type x = static_cast<integer_type>(0);
    for(size_type d = depth; d-- > 0;) {
      size_type cnt = mat[d].rank(right, 0) - mat[d].rank(left, 0);
      size_type k = (i < cnt) ? 0 : 1;
      x |= (k << d);
      left = mat[d].rank(left, k) + spl[d] * k;
      right = mat[d].rank(right, k) + spl[d] * k;
    }
    return x;
  }

  struct rank_result {
    size_type le;
    size_type eq;
    size_type mo;
  };

  // couting elements that less than x, equal to x, and more than x in range [left, right)
  rank_result rank_less_eq_more(size_type left, size_type right, integer_type x) const {
    size_type le = 0, mo = 0;
    for(size_type d = depth; d --> 0;) {
      size_type k = (x >> d) & 1;
      size_type l = mat[d].rank(left, 1);
      size_type r = mat[d].rank(right, 1);
      if(k == 0) {
        mo += r - l;
        left -= l;
        right -= r;
      }
      else {
        le += (right - left) - (r - l);
        left = l + spl[d];
        right = r + spl[d];
      }
    }
    return rank_result { le, right - left, mo };
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y, integer_type l, size_type d) const {
    integer_type r = l + (1 << d);
    if(x <= l && r <= y) {
      return right - left;
    }
    else if(y <= l || r <= x) {
      return 0;
    }
    else {
      d--;
      size_type lr = mat[d].rank(left, 1);
      size_type rr = mat[d].rank(right, 1);
      return
        rangefreq(left - lr, right - rr, x, y, l, d) +
        rangefreq(lr + spl[d], rr + spl[d], x, y, l + (1 << d), d);
    }
  }

  size_type rangefreq(size_type left, size_type right, integer_type x, integer_type y) const {
    return rangefreq(left, right, x, y, 0, depth);
  }
};

template<class T>
struct burrows_wheeler {
  std::vector<int> sa;
  std::vector<T> L;
  std::vector<int> C;
  wavelet_matrix<T> wm;
  burrows_wheeler(const std::vector<T>& s, int k, int de): sa(sa_is(s, k)), L(s.size() + 1), C(k + 1) {
    for(int i = 0;i < sa.size();i++) {
      if(sa[i] > 0) L[i] = s[sa[i] - 1];
      else L[i] = 0;
    }
    for(int i = 0;i < s.size();i++) {
      C[s[i] + 1]++;
    }
    C[0]++;
    for(int i = 0; i < k; i++) {
      C[i + 1] += C[i];
    }
    wm = wavelet_matrix<T>(L, de);
  }
  int match(const std::string& p) {
    int s = 0;
    int e = sa.size();
    for(int i = p.size();i --> 0;) {
      s = wm.rank_x(0, s, p[i]) + C[p[i]];
      e = wm.rank_x(0, e, p[i]) + C[p[i]];
      if(s >= e) break;
    }
    return e - s;
  }
};

Rolling Hash

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

const i64 rhmod = 1e9 + 7;
const i64 BASE_C = 2;
const i64 base[] = {9973, 10007};

struct rolling_hash {
  i64 n;
  vector<i64> hs[BASE_C], pw[BASE_C];
  
  template<class Array>
  rolling_hash(const Array& arr) {
    n = arr.size();
    for(int i = 0;i < BASE_C;i++) {
      hs[i].assign(n + 1, 0);
      pw[i].assign(n + 1, 0);
      hs[i][0] = 0;
      pw[i][0] = 1;
      for(int j = 0;j < n;j++) {
        pw[i][j + 1] = pw[i][j] * base[i] % rhmod;
        hs[i][j + 1] = (hs[i][j] * base[i] + arr[j]) % rhmod;
      }
    }
  }

  pair<i64, i64> hash(i64 l, i64 r) {
    return {
      ((hs[0][r + 1] - hs[0][l] * pw[0][r + 1 - l]) % rhmod + rhmod) % rhmod,
      ((hs[1][r + 1] - hs[1][l] * pw[1][r + 1 - l]) % rhmod + rhmod) % rhmod,
    };
  }
};

Graph

Dijkstra

Code

#include <vector>
#include <queue>

/*
 * delta(V v, fn (V t, W weight))
 * index(V v) -> int
 */
template<class V, class W, class Delta, class Index>
std::vector<W> dijkstra(std::size_t N, W inf, V s, Delta delta, Index index) {
  std::vector<W> dist(N, inf);
  using P = std::pair<W, V>;
  std::priority_queue<P, std::vector<P>, std::greater<P>> que;
  que.push({ dist[index(s)] = W(), s });
  while(!que.empty()) {
    W d = que.top().first;
    V v = que.top().second;
    que.pop();
    if(dist[index(v)] < d) continue;
    delta(v, [&](V t, W weight) {
        if(dist[index(t)] > dist[index(v)] + weight) {
          que.push({ dist[index(t)] = dist[index(v)] + weight, t });
        }
    });
  }
  return dist;
}

Dijkstra

Code

#include <vector>
#include <queue>

/*
 * delta(V v, fn (V t, W weight))
 * index(V v) -> int
 */
template<class V, class W, class Delta, class Index>
std::vector<W> dijkstra(std::size_t N, W inf, V s, Delta delta, Index index) {
  std::vector<W> dist(N, inf);
  using P = std::pair<W, V>;
  std::priority_queue<P, std::vector<P>, std::greater<P>> que;
  que.push({ dist[index(s)] = W(), s });
  while(!que.empty()) {
    W d = que.top().first;
    V v = que.top().second;
    que.pop();
    if(dist[index(v)] < d) continue;
    delta(v, [&](V t, W weight) {
        if(dist[index(t)] > dist[index(v)] + weight) {
          que.push({ dist[index(t)] = dist[index(v)] + weight, t });
        }
    });
  }
  return dist;
}

BFS

Code

#include <vector>
#include <queue>
using i64 = long long;

/*
 * delta(V v, fn (V t))
 * index(V v) -> int
 */
template<class V, class Delta, class Index>
std::vector<i64> bfs(std::size_t N, V s, Delta delta, Index index) {
  std::vector<i64> dist(N, -1);
  std::queue<V> que;
  dist[index(s)] = 0;
  que.push(s);
  while(!que.empty()) {
    V v = que.front();
    que.pop();
    delta(v, [&](V t) {
        if(dist[index(t)] == -1) {
          dist[index(t)] = dist[index(v)] + 1;
          que.push(t);
        }
    });
  }
  return dist;
}

Dial 01

Code

#include <vector>
#include <queue>
using i64 = long long;

/*
 * delta(V v, fn (V t, i64 weight))
 * index(V v) -> int
 */
template<class V, class W, class Delta, class Index>
std::vector<i64> dial01(std::size_t N, W inf, V s, Delta delta, Index index) {
  std::vector<i64> dist(N, inf);
  using P = std::pair<V, W>;
  std::deque<P> que;
  dist[index(s)] = 0;
  que.push_back({ s, dist[index(s)]});
  while(!que.empty()) {
    V v = que.front().first;
    W d = que.front().second;
    que.pop_front();
    if(dist[index(v)] < d) continue;
    delta(v, [&](V t, i64 weight) {
        if(dist[index(t)] > dist[index(v)] + weight) {
          dist[index(t)] = dist[index(v)] + weight;
          if(weight == 0) que.push_front({ t, dist[index(t)] });
          else que.push_back({ t, dist[index(t)] });
        }
    });
  }
  return dist;
}

Dinic

Code

#include <vector>
#include <queue>

struct dinic {
  using cap_type = int;
  const cap_type INF = 1e9;
  struct edge {
    int to;
    cap_type cap;
    int rev;
  };
  int n;
  std::vector<std::vector<edge>> g;

  dinic(int n): n(n), g(n) {}

  void add_edge(int from, int to, cap_type cap, cap_type rev_cap = 0) {
    g[from].push_back({ to, cap, (int)(g[to].size()) });
    g[to].push_back({ from, rev_cap, (int)(g[from].size() - 1) });
  }
  
  std::vector<int> level;
  std::vector<int> iter;
  cap_type dfs(const int s, const int v, cap_type mf) {
    if(s == v || mf == 0) return mf;
    for(int& i = iter[v]; i < g[v].size(); i++) {
      int t = g[v][i].to;
      edge& re = g[v][i];
      edge& e = g[t][re.rev];
      if(level[t] >= level[v] || e.cap == 0) continue;
      cap_type f = dfs(s, t, std::min(mf, e.cap));
      if(f == 0) continue;
      e.cap -= f;
      re.cap += f;
      return f;
    }
    return 0;
  }

  cap_type max_flow(int s, int t) {
    std::vector<int> que(n);
    cap_type flow = 0;
    while(true) {
      level.assign(n, -1);
      int qi = 0;
      int qr = 0;
      level[s] = 0;
      que[qr++] = s;
      while(qi < qr && level[t]) {
        int v = que[qi++];
        for(const auto& e: g[v]) {
          if(e.cap > 0 && level[e.to] == -1) {
            level[e.to] = level[v] + 1;
            que[qr++] = e.to;
          }
        }
      }
      if(level[t] == -1) break;
      iter.assign(n, 0);
      cap_type tmp;
      while((tmp = dfs(s, t, INF)) > 0) {
        flow += tmp;
      }
    }
    return flow;
  }
};

Dinic

Code

#include <vector>
#include <queue>

struct dinic {
  using cap_type = int;
  const cap_type INF = 1e9;
  struct edge {
    int to;
    cap_type cap;
    int rev;
  };
  int n;
  std::vector<std::vector<edge>> g;

  dinic(int n): n(n), g(n) {}

  void add_edge(int from, int to, cap_type cap, cap_type rev_cap = 0) {
    g[from].push_back({ to, cap, (int)(g[to].size()) });
    g[to].push_back({ from, rev_cap, (int)(g[from].size() - 1) });
  }
  
  std::vector<int> level;
  std::vector<int> iter;
  cap_type dfs(const int s, const int v, cap_type mf) {
    if(s == v || mf == 0) return mf;
    for(int& i = iter[v]; i < g[v].size(); i++) {
      int t = g[v][i].to;
      edge& re = g[v][i];
      edge& e = g[t][re.rev];
      if(level[t] >= level[v] || e.cap == 0) continue;
      cap_type f = dfs(s, t, std::min(mf, e.cap));
      if(f == 0) continue;
      e.cap -= f;
      re.cap += f;
      return f;
    }
    return 0;
  }

  cap_type max_flow(int s, int t) {
    std::vector<int> que(n);
    cap_type flow = 0;
    while(true) {
      level.assign(n, -1);
      int qi = 0;
      int qr = 0;
      level[s] = 0;
      que[qr++] = s;
      while(qi < qr && level[t]) {
        int v = que[qi++];
        for(const auto& e: g[v]) {
          if(e.cap > 0 && level[e.to] == -1) {
            level[e.to] = level[v] + 1;
            que[qr++] = e.to;
          }
        }
      }
      if(level[t] == -1) break;
      iter.assign(n, 0);
      cap_type tmp;
      while((tmp = dfs(s, t, INF)) > 0) {
        flow += tmp;
      }
    }
    return flow;
  }
};

GoldBerg Tarjan's Preflow Relabel

Code

#include <vector>
#include <iostream>
#include <tuple>
#include <bitset>
struct residual_graph {
    using cap_type = long long;
    struct edge {
        int to;
        cap_type cap;
        int rev;
        edge(int t = 0, cap_type c = 0, int r = 0): to(t), cap(c), rev(r) {}
    };
    int N;
    std::vector<int> iter;
    std::vector<std::tuple<int, int, cap_type>> pool;
    std::vector<edge> edges;
    residual_graph(int N): N(N), iter(N + 1) {}

    void add_edge(int from, int to, cap_type cap, cap_type rev_cap) {
        iter[from]++;
        iter[to]++;
        pool.emplace_back(from, to, cap);
    }
    void build() {
        for (int i = 0; i < N; i++) {
            iter[i + 1] += iter[i];
        }

        edges.resize(pool.size() * 2);

        for (auto &&p : pool) {
            int fi = --iter[std::get<0>(p)];
            int ti = --iter[std::get<1>(p)];
            edges[fi] = edge(std::get<1>(p), std::get<2>(p), ti);
            edges[ti] = edge(std::get<0>(p), 0, fi);
        }
    }
    inline edge &operator[](int i) {
        return edges[i];
    }
};

struct goldberg_tarjan {
    using cap_type = long long;
    struct edge {
        int to;
        cap_type cap;
        edge(int t, cap_type c, int r): to(t), cap(c) {}
    };

    int N;
    residual_graph G;
    std::vector<cap_type> exc;
    std::vector<int> h;
    std::vector<std::vector<int>> que;
    std::vector<int> qi;
    std::vector<int> ei;
    std::vector<int> hcnt;
    std::bitset<10002> inque;
    const cap_type INF = 1e18;

    goldberg_tarjan(int n): N(n), G(n) {}

    void add_edge(int from, int to, cap_type cap, cap_type rev_cap = 0) {
        G.add_edge(from, to, cap, rev_cap);
    }
    void build() {
        G.build();
    }

    void push(int from, int ei) {
        cap_type fl = std::min(exc[from], G[ei].cap);
        G[ei].cap -= fl;
        G[G[ei].rev].cap += fl;
        exc[from] -= fl;
        exc[G[ei].to] += fl;
    }

    void relabel(int v) {
        hcnt[h[v]]--;
        h[v] = N;

        for (int i = G.iter[v]; i < G.iter[v + 1]; i++) {
            auto &e = G[i];

            if (e.cap > 0 && h[v] > h[e.to] + 1) {
                ei[v] = i;
                h[v] = h[e.to] + 1;
            }
        }

        hcnt[h[v]]++;
    }

    int global_relabeling(int t) {
        std::fill(std::begin(h), std::end(h), N);
        std::fill(std::begin(hcnt), std::end(hcnt), 0);

        for (int i = 0; i < N; i++) {
            que[i].clear();
        }

        inque.reset();
        int i = 0, qr = 0;
        std::vector<int> &Q = qi;
        Q[qr++] = t;
        h[t] = 0;
        int hi = 0;

        while (i < qr) {
            int v = Q[i++];
            hi = h[v];
            hcnt[hi]++;

            if (exc[v] > 0 && v != t) {
                que[h[v]].emplace_back(v);
                inque.set(v);
            }

            for (int gi = G.iter[v]; gi < G.iter[v + 1]; gi++) {
                auto &e = G[gi];

                if (G[e.rev].cap > 0 && h[v] + 1 < h[e.to]) {
                    h[e.to] = h[v] + 1;
                    Q[qr++] = e.to;
                }
            }
        }

        std::copy(std::begin(G.iter), std::begin(G.iter) + N, std::begin(ei));
        std::fill(std::begin(qi), std::end(qi), 0);
        return hi;
    }

    void gap_relabeling(int g) {
        for (int v = 0; v < N; v++) {
            if (g < h[v] && h[v] < N) {
                hcnt[h[v]]--;
                h[v] = N;
            }
        }
    }


    cap_type max_flow(int s, int t) {
        exc.assign(N, 0);
        exc[s] = INF;
        h.assign(N, 0);
        int cnt = 0;

        que.resize(N);
        qi.assign(N, 0);
        ei.resize(N);
        hcnt.assign(N + 1, 0);

        global_relabeling(t);

        if (h[s] == N)
            return 0;

        for (int di = h[s]; di >= 0;) {
            if (qi[di] == que[di].size()) {
                di--;
                continue;
            }

            int v = que[di][qi[di]++];
            inque.reset(v);

            if (exc[v] == 0 || v == t)
                continue;

            for (int &i = ei[v]; i < G.iter[v + 1]; i++) {
                auto &e = G[i];

                if (e.cap > 0 && h[v] == h[e.to] + 1) {
                    push(v, i);

                    if (exc[e.to] > 0 && e.to != t && !inque.test(e.to)) {
                        que[h[e.to]].emplace_back(e.to);
                        inque.set(e.to);
                    }

                    if (exc[v] == 0)
                        break;
                }
            }

            if (exc[v] == 0)
                continue;

            relabel(v);

            if (h[v] < N) {
                di = h[v];
                que[h[v]].emplace_back(v);
                inque.set(v);
            }

            if (hcnt[di] == 0) {
                gap_relabeling(di);
                di--;
            }

            if (++cnt % (N * 4) == 0) {
                di = global_relabeling(t);
            }
        }

        return exc[t];
    }
};

Successive Shortest Path

Code

#include <vector>
#include <queue>
#include <set>
using i64 = long long;

struct dinic {
  using capacity_type = i64;
  struct edge {
    i64 from;
    i64 to;
    capacity_type cap;
    capacity_type cost;
    i64 rev;
  };
  i64 n;
  i64 s, t;
  std::vector<std::vector<edge>> g;

  dinic(i64 n, i64 s, i64 t): n(n), s(s), t(t), g(n) {}
  void add_edge(i64 from, i64 to, i64 cap, i64 cost) {
    g[from].push_back({ from, to, cap, cost, (i64)(g[to].size()) });
    g[to].push_back({ to, from, 0, -cost, (i64)(g[from].size() - 1) });
  }
  std::vector<int> level;
  std::vector<int> iter;
  capacity_type dinic_dfs(int v, capacity_type f) {
    if(v == t) return f;
    else {
      capacity_type now = f;
      for(int& i = iter[v]; i < g[v].size(); i++) {
        auto& e = g[v][i];
        if(e.cap > 0 && level[e.to] > level[e.from]) {
          capacity_type c = std::min(now , e.cap);
          capacity_type d = dinic_dfs(e.to, c);
          e.cap -= d;
          g[e.to][e.rev].cap += d;
          now -= d;
          if(now == 0) return f - now;
        }
      }
      return f - now;
    }
  }

  capacity_type min_cost_flow(capacity_type f) {
    std::vector<capacity_type> po(n,0);
    capacity_type ans = 0;
    auto sel = [&](const edge& e) {
      return e.cost + po[e.from] - po[e.to];
    };
    while(f > 0) {
      std::vector<capacity_type> dist(n,0);
      std::vector<bool> vis(n,false);
      std::vector<int> p(n,-1);
      std::vector<int> pe(n,-1);
      using P = std::pair<capacity_type,int>;
      std::priority_queue<P,std::vector<P>,std::greater<P>> que;
      que.push({dist[s] ,s});
      vis[s] = true;
      while(!que.empty()) {
        int v = que.top().second;
        capacity_type d = que.top().first;
        que.pop();
        if(dist[v] < d) continue;
        for(int i = 0;i < g[v].size();i++) {
          auto& e = g[v][i];
          int u = e.to;
          if(e.cap == 0) continue;
          if(!vis[u] || dist[u] > dist[v] + sel(e)) {
            vis[u] = true;
            dist[u] = dist[v] + sel(e);
            p[u] = v;
            pe[u] = i;
            que.push({dist[u] , u});
          }
        }
      }
      if(p[t] == -1) break;

      capacity_type ff = f;
      for(int u = t;u != s;u = p[u])
        ff = std::min(ff, g[p[u]][pe[u]].cap);
      for(int u = t;u != s;u = p[u]) {
        ans += ff * g[p[u]][pe[u]].cost;
        g[p[u]][pe[u]].cap -= ff;
        g[u][g[p[u]][pe[u]].rev].cap += ff;
      }
      f -= ff;
      for(int i = 0;i < n;i++)
        po[i] += dist[i];
    }
    if(f != 0) return -1;
    return ans;
  }
};

Successive Shortest Path

Code

#include <vector>
#include <queue>
#include <set>
using i64 = long long;

struct dinic {
  using capacity_type = i64;
  struct edge {
    i64 from;
    i64 to;
    capacity_type cap;
    capacity_type cost;
    i64 rev;
  };
  i64 n;
  i64 s, t;
  std::vector<std::vector<edge>> g;

  dinic(i64 n, i64 s, i64 t): n(n), s(s), t(t), g(n) {}
  void add_edge(i64 from, i64 to, i64 cap, i64 cost) {
    g[from].push_back({ from, to, cap, cost, (i64)(g[to].size()) });
    g[to].push_back({ to, from, 0, -cost, (i64)(g[from].size() - 1) });
  }
  std::vector<int> level;
  std::vector<int> iter;
  capacity_type dinic_dfs(int v, capacity_type f) {
    if(v == t) return f;
    else {
      capacity_type now = f;
      for(int& i = iter[v]; i < g[v].size(); i++) {
        auto& e = g[v][i];
        if(e.cap > 0 && level[e.to] > level[e.from]) {
          capacity_type c = std::min(now , e.cap);
          capacity_type d = dinic_dfs(e.to, c);
          e.cap -= d;
          g[e.to][e.rev].cap += d;
          now -= d;
          if(now == 0) return f - now;
        }
      }
      return f - now;
    }
  }

  capacity_type min_cost_flow(capacity_type f) {
    std::vector<capacity_type> po(n,0);
    capacity_type ans = 0;
    auto sel = [&](const edge& e) {
      return e.cost + po[e.from] - po[e.to];
    };
    while(f > 0) {
      std::vector<capacity_type> dist(n,0);
      std::vector<bool> vis(n,false);
      std::vector<int> p(n,-1);
      std::vector<int> pe(n,-1);
      using P = std::pair<capacity_type,int>;
      std::priority_queue<P,std::vector<P>,std::greater<P>> que;
      que.push({dist[s] ,s});
      vis[s] = true;
      while(!que.empty()) {
        int v = que.top().second;
        capacity_type d = que.top().first;
        que.pop();
        if(dist[v] < d) continue;
        for(int i = 0;i < g[v].size();i++) {
          auto& e = g[v][i];
          int u = e.to;
          if(e.cap == 0) continue;
          if(!vis[u] || dist[u] > dist[v] + sel(e)) {
            vis[u] = true;
            dist[u] = dist[v] + sel(e);
            p[u] = v;
            pe[u] = i;
            que.push({dist[u] , u});
          }
        }
      }
      if(p[t] == -1) break;

      capacity_type ff = f;
      for(int u = t;u != s;u = p[u])
        ff = std::min(ff, g[p[u]][pe[u]].cap);
      for(int u = t;u != s;u = p[u]) {
        ans += ff * g[p[u]][pe[u]].cost;
        g[p[u]][pe[u]].cap -= ff;
        g[u][g[p[u]][pe[u]].rev].cap += ff;
      }
      f -= ff;
      for(int i = 0;i < n;i++)
        po[i] += dist[i];
    }
    if(f != 0) return -1;
    return ans;
  }
};

Strongly Connected Components

Code

#include <vector>
#include <iostream>
 
struct strongly_connected_components {
  std::vector<std::vector<int>> g;
  std::vector<std::vector<int>> rg;
  std::vector<int> vis;
  std::vector<int> vs;
 
  std::vector<int> group;
  std::vector<std::vector<int>> comps;
  int groups;
 
  strongly_connected_components(int N): g(N), rg(N), vis(N, 0), group(N, -1) {}
 
  void add_edge(int a, int b) {
    g[a].push_back(b);
    rg[b].push_back(a);
  }
 
  void dfs(int v) {
    vis[v] = 1;
    for(auto& t: g[v]) {
      if(!vis[t]) dfs(t);
    }
    vs.push_back(v);
  }
 
  void rdfs(int v, int k) {
    vis[v] = 1;
    group[v] = k;
    comps.back().push_back(k);
    for(auto to: rg[v]) {
      if(!vis[to]) rdfs(to, k);
    }
  }
 
  void build() {
    for(int i = 0; i < g.size(); i++) {
      if(!vis[i]) dfs(i);
    }
    vis.assign(g.size(), false);
    groups = 0;
    for(int i = g.size(); i --> 0; ) {
      if(!vis[vs[i]]) {
        comps.push_back(std::vector<int>());
        rdfs(vs[i], groups++);
      }
    }
  }
 
  std::vector<std::vector<int>> build_compressed_graph() {
    std::vector<std::vector<int>> cg(groups);
    for(int i = 0; i < g.size(); i++) {
      for(auto& j: g[i]) {
        cg[group[i]].push_back(group[j]);
      }
    }
    return cg;
  }
};

Incremental Bridge Connectivity

Code

#include <vector>
#include <tuple>
#include <bitset>
#include <iostream>


struct union_find {
  std::vector<int> par;
  union_find(int N): par(N, -1) {}
  int root(int x) {
    return par[x] < 0 ? x : par[x] = root(par[x]);
  }
  std::tuple<int, int> unite(int x, int y) {
    x = root(x);
    y = root(y);
    if(x == y) return std::make_tuple(x, y);
    if(par[x] > par[y]) std::swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return std::make_tuple(x, y);
  }
  int size(int x) {
    return -par[root(x)];
  }
};


struct incremental_bridge_connectivity {
  static const int MAX_N = 202020;
  static const int MAX_E = 202020;
  std::vector<int> par;
  std::vector<std::pair<int, int>> edge;
  std::bitset<MAX_N> bit;
  std::bitset<MAX_E> bridge;
  union_find cg;
  union_find bcg;

  incremental_bridge_connectivity(int N)
    : par(N, -1), cg(N), bcg(N) {
    }

  int lca(int u, int v) {
    bit = 0;
    while(1) {
      if(u >= 0) {
        if(bit.test(u)) {
          return u;
        }
        bit.set(u);
        if(par[u] < 0) u = -1;
        else u = edge[par[u]].second;
      }
      std::swap(u, v);
    }
  }

  void compress_path(int v, int w) {
    while(v != w) {
      bcg.unite(v, w);
      bridge.reset(par[v]);
      v = edge[par[v]].second;
    }
  }

  void reverse_path(int v, int p) {
    while(true) {
      std::swap(edge[p].first, edge[p].second);
      std::swap(p, par[v]);
      if(p < 0) break;
      v = edge[p].second;
    }
  }

  int add_edge(int u, int v) {
    int ei = edge.size();
    edge.emplace_back(u, v);
    if(bcg.root(u) == bcg.root(v)) {
    }
    else if(cg.root(u) == cg.root(v)) {
      int w = lca(u, v);
      compress_path(u, w);
      compress_path(v, w);
    }
    else {
      bridge.set(ei);
      if(cg.size(u) < cg.size(v)) {
        std::swap(u, v);
        std::swap(edge.back().first, edge.back().second);
      }
      reverse_path(v, ei);
      cg.unite(u, v);
    }
    return ei;
  }

  int size(int v) {
    return bcg.size(v);
  }

  bool is_bridge(int ei) {
    return bridge.test(ei);
  }
};

Lattice Graph

Code

#include <set>
#include <vector>
using i64 = long long;

template<class F>
struct lattice_delta {
  i64 H, W;
  F f;
  using P = std::pair<i64, i64>;
  lattice_delta(i64 H, i64 W, F f): H(H), W(W), f(f) {}
  template<class Func>
  void operator()(P v, Func func) {
    const static std::vector<i64> dx { 1, 0, -1, 0 };
    i64 i = v.first;
    i64 j = v.second;
    for(i64 q = 0; q < 4; q++) {
      i64 x = i + dx[q];
      i64 y = j + dx[q ^ 1];
      if(0 <= x && x < H && 0 <= y && y < W) {
        f(P(i, j), P(x, y), func);
      }
    }
  }
};
 
template<class F>
lattice_delta<F> make_lattice_delta(i64 H, i64 W, F f) { return lattice_delta<F>(H, W, f); }
 
struct lattice_index {
  i64 H, W;
  using P = std::pair<i64, i64>;
  lattice_index(i64 H, i64 W): H(H), W(W) {}
  i64 operator()(P v) {
    return v.first * W + v.second;
  }
};

Geometry

昔作ったやつが残っていたのでとりあえず貼っておく 要整備

Spec

  • Convex_Hull
    • 凸包

Code

#include <algorithm>
#include <cmath>
#include <tuple>
#include <vector>
using namespace std;

using ld = long double;

const ld EPS = 1e-8;
inline bool eq(ld a, ld b) { return abs(a - b) < EPS; }
const ld PI = acos(-1);

namespace Geometory {
  struct Point {
    ld x, y;
    Point(ld x = 0, ld y = 0) : x(x), y(y) {}
    Point operator+(const Point& b) const { return Point(x + b.x, y + b.y); }
    Point operator-(const Point& b) const { return Point(x - b.x, y - b.y); }
    Point operator*(const ld b) const { return Point(x * b, y * b); }
    Point operator/(const ld b) const { return Point(x / b, y / b); }
    bool operator<(const Point& b) const {
      if (x != b.x)
        return x < b.x;
      else
        return y < b.y;
    }
    bool operator==(const Point& b) const { return eq(x, b.x) && eq(y, b.y); }
    ld norm() const { return x * x + y * y; }
    ld abs() const { return sqrt(norm()); }
    ld arg() const { return atan2(x, y); }
    Point rotate(const ld theta) const {
      ld co = cos(theta);
      ld si = sin(theta);
      return Point(co * x - si * y, si * x + y * co);
    }
    Point rotate90() const { return Point(-y, x); }
  };

  ld dot(const Point& a, const Point& b) { return a.x * b.x + a.y * b.y; }
  ld cross(const Point& a, const Point b) { return a.x * b.y - a.y * b.x; }

  struct Line {
    Point from, to;
    Line(Point from = Point(), Point to = Point()) : from(from), to(to) {}
  };

  struct Segment {
    Point from, to;
    Segment(Point from = Point(), Point to = Point()) : from(from), to(to) {}
  };

  bool is_orthogonal(const Line& la, const Line& lb) {
    return eq(0.0, dot(la.from - la.to, lb.from - lb.from));
  }
  bool is_parallel(const Line& la, const Line& lb) {
    return eq(0.0, cross(la.from - la.to, lb.from - lb.from));
  }
  bool is_Point_on(const Line& l, const Point& p) {
    return eq(0.0, cross(l.to - l.from, p - l.from));
  }
  bool is_Point_on(const Segment& s, const Point& p) {
    return (s.from - p).abs() + (p - s.to).abs() < (s.from - s.to).abs() + EPS;
  }
  ld distance(const Line& l, const Point& p) {
    return abs(cross(l.to - l.from, p - l.from)) / (l.to - l.from).abs();
  }
  ld distance(const Segment& s, const Point& p) {
    if (dot(s.to - s.from, p - s.from) < EPS) return (p - s.from).abs();
    if (dot(s.from - s.to, p - s.to) < EPS) return (p - s.to).abs();
    return abs(cross(s.to - s.from, p - s.from)) / (s.to - s.from).abs();
  }
  ld is_intersected(const Segment& a, const Segment& b) {
    return (cross(a.to - a.from, b.from - a.from) *
                cross(a.to - a.from, b.to - a.from) <
            EPS) &&
           (cross(b.to - b.from, a.from - b.from) *
                cross(b.to - b.from, a.to - b.from) <
            EPS);
  }

  ld is_intersected(const Segment& s, const Line& l) {
    // line -> ax + by + c = 0
    ld a = l.to.y - l.from.y;
    ld b = l.from.x - l.to.x;
    ld c = -a * l.from.x - b * l.from.y;
    ld t1 = a * s.from.x + b * s.from.y + c;
    ld t2 = a * s.to.x + b * s.to.y + c;
    return t1 * t2 <= 0;
  }

  Point intersection_point(const Segment& a, const Segment& b) {
    Point bp = b.to - b.from;
    ld d1 = abs(cross(bp, a.from - b.from));
    ld d2 = abs(cross(bp, a.to - b.from));
    ld t = d1 / (d1 + d2);
    return a.from + (a.to - a.from) * t;
  }

  Point intersection_point(const Line& a, const Line& b) {
    Point ap = a.to - a.from;
    Point bp = b.to - b.from;
    return a.from + ap * cross(bp, b.from - a.from) / cross(bp, ap);
  }
  // counterclockwise
  int ccw(const Point& a, const Point& b, const Point& c) {
    Point ba = b - a;
    Point ca = c - a;
    if (cross(ba, ca) > EPS) return 1;   // a - b --/ c
    if (cross(ba, ca) < EPS) return -1;  // a - b --| c
    if (dot(ba, ca) < 0) return 2;       // b - a - c
    if (b.norm() < c.norm()) return -2;  // a - b - c
    return 0;                            // a -- c -- b
  }

  vector<Point> Convex_Hull(vector<Point>& p) {
    int n = p.size();
    int k = 0;
    if (n >= 3) {
      sort(p.begin(), p.end());
      vector<Point> ch(2 * n);
      for (int i = 0; i < n; ch[k++] = p[i++]) {
        while (k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) <= 0)
          k--;
      }
      for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
        while (k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) <= 0)
          k--;
      }
      ch.resize(k - 1);
      return ch;
    } else {
      return p;
    }
  }
};

Tech

テクニック, 分類できなかったもの

Compression

座標圧縮をする.

Spec

  • add(T x)

    • 要素xを追加する
  • build()

    • 準備
  • comp(T x)

    • 座圧した結果を返す
  • comp(vector<T> x)

    • まとめて座圧
    • x = comp(std::move(x))すると楽です.

Code

#include <vector>

template<class T>
struct Compression {
  using size_type = std::size_t;

  std::vector<T> v;

  Compression(){}
  void add(const T& t) { v.push_back(t); }
  void build() {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
  }
  size_type comp(const T& x) const {
    return lower_bound(begin(v), end(v), x) - begin(v);
  }
  size_type size() const { return v.size(); }
};

Grundy Number

Spec

  • grn[(ゲームの状態)]0 -> その状態での手番の人が負ける
  • grn[(ゲームの状態)]0以外 -> その状態での手番の人が勝つ

Code

このコードでは, 取れる個数に制限がついているNimの勝敗を計算している.

/*
 N個の正整数からなる集合 A={a1,a2,…,aN}があります。 太郎君と次郎君が次のゲームで勝負します。

最初に、K個の石からなる山を用意します。 二人は次の操作を交互に行います。 先手は太郎君です。
Aの元 xをひとつ選び、山からちょうど x個の石を取り去る。
先に操作を行えなくなった人が負けです。 二人が最適に行動すると仮定したとき、どちらが勝つかを判定してください。
*/

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

vector<i64> grn(101010, -1);

i64 N, K;
vector<i64> a;

int main() {
  cin >> N >> K;
  a.resize(N);
  for(int i = 0;i < N;i++) cin >> a[i];
  for(i64 k = 0;k <= K;k++) {
    set<i64> st;
    for(auto x: a) {
      if(k - x >= 0) st.insert(grn[k - x]);
    }
    grn[k] = K + 1;
    for(int i = 0;i <= K + 1;i++) {
      if(!st.count(i)) {
        grn[k] = i;
        break;
      }
    }
  }
  if(grn[K]) {
    cout << "First" << endl;
  }
  else {
    cout << "Second" << endl;
  }
}

Monge DP

Mo's Algorithm

もーもー

Code

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define rep(i,s,e) for(i64 (i) = (s);(i) < (e);(i)++)
#define rev(i,s,e) for(i64 (i) = (e); (i)-- > (s);)
#define all(x) x.begin(),x.end()

struct Mo {
  vector<i64> left, right, order;
  vector<bool> v;
  

  const i64 width;
  i64 nl, nr, ptr;

  vector<i64> a;
  vector<i64> cnt;
  i64 ans;

  Mo(i64 n, vector<i64> a) : v(n), width((i64)sqrt(n)), nl(0), nr(0), ptr(0), a(a), cnt(1010101), ans(0) {}

  void add_query(i64 l, i64 r) {
    left.push_back(l);
    right.push_back(r);
  }

  void build() {
    order.resize(left.size());
    for(i64 i = 0;i < left.size();i++) order[i] = i;
    sort(begin(order), end(order), [&](i64 a, i64 b) {
        if(left[a] / width != left[b] / width) return left[a] < left[b];
        else return right[a] < right[b];
        });
  }


  void add(i64 idx) {
    if(cnt[a[idx]]++ == 0) ans++;
  }
  void del(i64 idx) {
    if(--cnt[a[idx]] == 0) ans--;
  }

  inline void distribute(i64 idx) {
    v[idx].flip();
    if(v[idx]) add(idx);
    else del(idx);
  }

  i64 process() {
    if(ptr == order.size()) return -1;
    const auto id = order[ptr];
    while(nl > left[id]) distribute(--nl);
    while(nr < right[id]) distribute(nr++);
    while(nl < left[id]) distribute(nl++);
    while(nr > right[id]) distribute(--nr);
    return order[ptr++];
  }
};

Slide Min

FastIO

Spec

Code

#include <unistd.h>

namespace niu {

  struct fastin {
    static const int bufsize = 1 << 24;
    char buf[bufsize];
    char* iter;
    fastin() {
      iter = buf;
      for(int t = 0, k; (k = read(STDIN_FILENO, buf + t, sizeof(buf)) - t) > 0; t += k);
    }
    fastin& operator>>(int& num) {
      num = 0;
      bool neg = false;
      while(*iter < '+') iter++;
      if(*iter == '-') { neg = true; iter++; }
      else if(*iter == '+') iter++;
      while(*iter >= '0') num = 10 * num + *(iter++) - '0';
      if(neg) num = -num;
      return *this;
    } 
  } fin;
  struct fastout {
    static const int bufsize = 1 << 24;
    char buf[bufsize];
    char* iter;
    fastout() {
      iter = buf;
    }
    ~fastout() {
      for(int t = 0, k; (k = write(STDOUT_FILENO, buf + t, iter - buf - t)) > 0; t += k);
    }
    fastout& operator<<(int num) {
      static char tmp[20];
      if(num == 0) {
        *(iter++) = '0';
        return *this;
      }
      if(num < 0) {
        *(iter++) = '-';
        num = -num;
      }
      int i = 0;
      while(num) {
        tmp[i++] = num % 10;
        num /= 10;
      }
      while(i--) {
        *(iter++) = tmp[i] + '0';
      }
      return *this;
    }
    fastout& operator<<(char c) {
      *(iter++) = c;
      return *this;
    }
  } fout;
}

Rerooting

  • T ... 答え
  • E ... 辺の型
  • U ... 辺から伸ばしたときの値, 可換モノイド
  • To ... E -> V 頂点番号の取得
  • Adj ... T x E -> U 辺から伸ばす
  • Ope ... U x U -> U モノイドの値
  • Merge ... U x V -> T モノイドと頂点から答えを導く

Code

#include <vector>
 
template<class T, class E, class U, class To, class Adj, class Ope, class Merge>
std::vector<T> rerooting(const std::vector<std::vector<E>>& G, int r, U ide, To to, Adj adj, Ope ope, Merge merge) {
  int N = G.size();
  std::vector<T> tdp(N);
  std::vector<int> par(N, -1);
  std::vector<int> que(N);
  int qi = 0;
  que[qi++] = r;
 
  int MAX = 1;
  for(int i = 0;i < qi;i++) {
    int v = que[i];
    for(const auto& e: G[v]) {
      int t = to(e);
      if(t == par[v]) continue;
      par[t] = v;
      que[qi++] = t;
    }
    MAX = std::max(MAX, (int)G[v].size() + 1);
  }
 
  for(int i = qi; i --> 0;) {
    int v = que[i];
    U sum = ide;
    for(const auto& e : G[v]) {
      int t = to(e);
      if(t == par[v]) continue;
      sum = ope(sum, adj(tdp[t], e));
    }
    tdp[v] = merge(sum, v);
  }
 
  std::vector<U> f(MAX);
  std::vector<U> b(MAX);
 
  for(int i = 0; i < qi; i++) {
    int v = que[i];
    for(int j = 0;j < G[v].size();j++) {
      int t = to(G[v][j]);
      f[j + 1] = adj(tdp[t == par[v] ? v : t], G[v][j]);
    }
    b[G[v].size()] = ide;
    for(int j = G[v].size(); j --> 0;) b[j] = ope(f[j + 1], b[j + 1]);
    f[0] = ide;
    for(int j = 0;j < G[v].size(); j++) {
      int t = to(G[v][j]);
      f[j + 1] = ope(f[j], f[j + 1]);
      if(t != par[v]) {
        tdp[t] = merge(ope(f[j], b[j + 1]), v);
      }
    }
    tdp[v] = merge(f[G[v].size()], v);
  }
  return tdp;
}