noshi91のメモ

データ構造のある風景

「あーだこーだー」 第三回 非公式まとめ

注意: この記事は非公式です。また、情報の正確性や取捨選択について著者は一切の保証をしません。

概要

コンテスト関係で重要と思われる情報を抜粋、要約しました。 雑談などの情報は省略、あるいは大筋のみになっています。

まとめ

AtCoderの公式生放送「あーだこーだー」 第三回

言語アップデート関連

7:19~

  • ジャッジアップデートがほぼ終わった
  • 4/4 の ABC は今まで通りのジャッジシステム
  • 4/5 にテストコンテストを開催予定
  • 早ければ 4/11 頃の ABC から新しい言語で開催予定

AtCoder の日本人登録者数が 10 万人を突破

12:37~

AtCoder が IT 人材採用に関する調査を実施

17:38~

岩田陽一さんが AtCoder の新入社員に

19:50~

  • 主な業務はマラソン部門の運営
  • 今までで言う Chokudai Contest などが rated で展開するかもしれない
  • ラソンコンテストが増えることは間違いない

コンテスト予定

21:43~

  • 4 月中に AGC を 2 回開きたいが怪しい
  • 5 月に入ってから ARC 級が 2 つ予定されている

maroonrk さんが admin になる件について

25:50~

  • しばらくは rng_58 さんが補佐的な感じで入る
  • 変わる部分はあるが、AtCoder の思想が大幅に変わることはない

rating が積み上げ式になる件について (マラソンの rating の文脈のようです)

26:17~

  • まだ確定ではない
  • 現在議論中

AtCoderJobs 関連

26:49~

  • 22 年新卒の求人が追加された

PAST 関連

32:28~

  • 3/26 に「PAST エントリー取得講座」を開催した
  • 4/9 に初級講座を開催予定

高橋直大氏が NewsPicks のプロピッカーに選ばれた

39:27~

FacebookAtCoder ページが開設

41:10

ジャッジサーバーがちょっと良くなったので、全体的に少し速くなる

45:43~

コラボについて

54:40~

  • じょえちゃんねる、AtCoderProblems、採用担当などアイデアはある
  • 今は情勢が厳しい

ABC の解説放送の場所について

54:58~

  • 家か会社かは微妙
  • すぬけさん次第

AtCoder はもうすぐ 8 周年

57:18~

オムライス

概要

オムライスを作り、食べた

f:id:noshi91:20210513214145j:plain
オムライス

作り方

みじん切りにした玉ねぎを炒める

鶏もも肉を加えて炒める

ケチャップを加えて炒める

炊いた白米を加えて炒める

チキンライス完成

卵を焼く

チキンライスの上に乗せる

オムライス完成

食べる

結果

おいしい

重要

玉ねぎの炒め無さ過ぎに注意すれば大体何やってもおいしい

競プロ用 C++ struct 超初級

概要

競プロ用の C++ ライブラリを作るための struct の使い方を説明する


メンバ変数

struct を使うと、std::pair<int, int> と似たようなものを作ることが出来ます。

#include <iostream>

struct t {
  int a;
  int b;
};

int main() {
  t x;
  x.a = 1;
  x.b = 2;
  std::cout << x.a << " " << x.b; // 1 2 と表示される
}

struct 型の名前 { }; が基本の構文です (最後のセミコロン忘れに注意)。 波括弧の中に書かれた変数をメンバ変数と呼び、x.a のようにして使用できます。


メンバ関数

このままでは芸がないので、a と b の両方に同じ値を代入する機能を付けてみます。

#include <iostream>

struct t {
  int a;
  int b;

  void f(int v) {
    a = v;
    b = v;
  }
};

int main() {
  t x;
  x.f(3);
  std::cout << x.a << " " << x.b; // 3 3 と表示される
}

波括弧の中に書いた関数をメンバ関数と呼び、x.f() のようにして呼び出すことが出来ます。 メンバ関数内ではメンバ変数やメンバ関数を自由に使うことが出来ます。


コンストラク

std::vector a(3, 1); のように、宣言したときに struct の中身を決められると便利です。 t x(v); とすると a が v で、b がその 2 倍で初期化されるようにしてみます。

#include <iostream>

struct t {
  int a;
  int b;

  t(int v) {
    a = v;
    b = 2 * v;
  }
};

int main() {
  t x(4);
  std::cout << x.a << " " << x.b << " " << t(3).b; // 4 8 6 と表示される
}

型名(引数){処理} のように書いたものをコンストラクタと呼び、変数を宣言する時や、その型の値を作る時に呼ばれる関数になります。 メンバ関数とよく似た記法ですが、少しだけ異なります。


コンストラクタでのメンバ変数の初期化

コンストラクタの節で書いたような方法では、メンバ変数が初期化されたのちコンストラクタ内で再び代入されます *1。 これは効率が良くないので、いきなり値を決めて初期化する機能が存在します。

#include <iostream>

struct t {
  int a;
  int b;

  t(int v) : a(v), b(2 * v) {}
};

int main() {
  t x(4);
  std::cout << x.a << " " << x.b; // 4 8 と表示される
}

コンストラクタの引数の閉じ括弧の後ろに : を書き、変数名(引数) のように書きます。 この初期化ではメンバ変数のコンストラクタが呼ばれています。 よって次のようなことも出来ます。

#include <iostream>
#include <vector>

struct t {
  int a;
  std::vector<int> b;

  t(int n) : a(n), b(n, 1) {}
};

int main() {
  t x(2);
  std::cout << x.a << " " << x.b[0] << " " << x.b[1]; // 2 1 1 と表示される
}

t のコンストラクタに n を与えると、a は n、b は n 個の 1 で初期化されます。


Union Find Tree を書いてみる

簡単にするため、経路圧縮だけのものを実装してみます。

#include <iostream>
#include <vector>

struct union_find {
  std::vector<int> par;

  union_find(int n) : par(n) {
    for (int i = 0; i < n; i++) {
      par[i] = i;
    }
  }

  int find(int x) {
    if (par[x] == x)
      return x;
    else {
      par[x] = find(par[x]);
      return par[x];
    }
  }

  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    par[y] = x;
  }
};

int main() {
  union_find uf(3);
  uf.unite(1, 0);
  std::cout << uf.find(0); // 1 と表示される
}


template

a や b が int 以外のものも欲しくなるかもしれません。 しかし、double や long など、全ての型について同じものを書くのは大変です。 template と呼ばれる機能を使うことで解決されます。

#include <iostream>

template <class T>
struct t {
  T a;
  T b;
};

int main() {
  t<int> x;
  t<double> y;
  x.a = 3.14;
  y.a = 3.14;
  std::cout << x.a << " " << y.a; // 3 3.14 と表示される
}

template と最初に付け、使う時に 型名<型引数> とすることで、任意の型に対して使用することが出来ます。 型引数と書いた通り、これは関数と類似した機能です。 引数の個数を増やしたりできます。

#include <iostream>

template <class T, class U>
struct t {
  T a;
  U b;
};

int main() {
  t<int, double> x;
  x.a = 2.72;
  x.b = 2.72;
  std::cout << x.a << " " << x.b; // 2 2.72 と表示される
}

a と b の型が異なる場合も自由に使えるようになりました。 よく見ると、std::pair と似たような機能、使い方になっています。 std::pair を始めとする標準ライブラリの便利な型の数々も、template を用いて実装されています。


const メンバ関数

a と b の合計を取得するメンバ関数 sum を書いてみます。 このメンバ関数は a や b の値を変化させません。 const メンバ関数という機能を使うと、書き換えをコンパイルエラーにすることが出来ます。

#include <iostream>

struct t {
  int a;
  int b;

  int sum() const {
    // a = 3; // コンパイルエラーになる
    return a + b;
  }
};

int main() {
  t x;
  x.a = 2;
  x.b = 3;
  std::cout << x.sum(); // 5 と表示される
}

メンバ関数の引数の閉じ括弧の後ろに const を付けると const メンバ関数となります。 間違えて書き換えてしまうことを防止する利点があります。


private

struct 内部でだけ使うメンバ変数やメンバ関数を書くこともあります。 外部から触れないようにできれば、誤用を防止できて便利です。

struct t {
  int a;

private:
  int b;
};

int main() {
  t x;
  x.a = 1;
  // x.b = 2; // コンパイルエラーになる
}

private: と書くと、そこから下に書いたメンバ変数やメンバ関数は外部から触れなくなります。 逆に public: と書くと、外部から触れるようになります。 struct はデフォルトで public なので、何も書かないと外部から全て触れる状態になります。


class

struct とよく似た機能に class が存在します。 この 2 者の違いはデフォルトで public か private かしかありませんので、好きな方を使えばよいです。


static メンバ変数 / static メンバ関数

値ではなく型そのものに紐づいた変数や関数を使いたいときに使用します。 割愛。

*1:この例だと int なので未初期化の値になっているのですが、事情が面倒なので割愛します

競プロのアルゴリズム関連略称まとめ

随時募集しています

略称 正称
APSP All Pairs Shortest Path
BB Branch and bound
BBST Balanced Binary Search Tree
BFS Breadth First Search
BIT Binary Indexed Tree
BM Berlekamp-Massey
BM Boyer-Moore
BSGS Baby-Step Giant-Step
CHT Convex Hull Trick
CRT Chinese Remainder Theorem
D&C Divide and Conquer
DAG Directed Acyclic Graph
DEPQ Double-ended priority queue
DFS Depth First Search
DP Dynamic Programming
DST Disjoint Sparse Table
DSU Disjoint Set Union
ET Euler Tour
ETT Euler Tour Tree
FFT Fast Fourier Transform
FMT Fast Modulo Transform
FPS Formal Power Series
HLD Heavy Light Decomposition
IP Integer Programming
KMP Knuth-Morris-Pratt
LA Level Ancestor
LCA Lowest Common Ancestor
LCP Longest Common Prefix
LCS Longest Common Subsequence
LCT Link Cut Tree
LCT Li Chao Tree
LIS Longest Increasing Subsequence
LLRBT Left-Leaning Red-Black Tree
LP Linear Programming
MC Max Clique Problem
MCF Minimum Cost Flow
MIS Maximal independent set
MST Minimum Spanning Tree
NTT Number Theoretic Transform
PQ Priority Queue
RBST Randomized Binary Search Tree
RBT Red-Black Tree
RLE Run Length Encoding
RMQ Range Minimum Query
RSQ Range Sum Query
RUQ Range Update Query
SA Suffix Array
SAT satisfiability problem
SCC Strongly Connected Components
SPFA Shortest Path Faster Algorithm
SSP Shortest Superstring Problem
SSP Successive Shortest Path
SSSP Single Source Shortest Path
SWAG Sliding Window Aggregation
TSP Travelling salesman problem
UF Union Find
WF Warshall Floyd
WM Wavelet Matrix

みんぷろ2018決勝-A Uncommon Θ(Alog(log(A)))

A - Uncommon

これを  A := \max(a_i,m) として時間計算量  \Theta(A \log(\log(A) ) ) で解きます。

 \gcd 畳み込み

ゼータ変換・メビウス変換を理解する - Qiita
添え字 gcd での畳み込みで AGC038-C を解く - noshi91のメモ
添え字の  \gcd での畳み込みをおおまかに理解しているとします。  g(x) := \sum_{x|d} f(d) は行列  M を用いて  g = Mf と表現できます。 これを軸にして式変形を行います。

値の定義と記法

添え字は 1-indexed
アイバーソンの記法 - Wikipedia

  • 行列  M
     M_{i,j} := \lbrack i | j \rbrack
  • 単位ベクトル  e_i
     (e_i)_j := \lbrack i = j \rbrack
  • 入力  a を表すベクトル  v
     v_i := \# \lbrace j \mid a_j = i \rbrace
  • ベクトル同士の各点積  \circ
     (x \circ y)_i := x_i y_i
    演算子の優先度は行列積より低いとする

例えば、 \gcd 畳み込みは  M ^{-1}(Mx \circ My) となる。
また、 1 × N 行列と  N 次元ベクトルの同一視などを断りなく行う。

アルゴリズム

 i についての答えは以下のように書くことが出来る。
 (M^{-1}(Mv \circ Me_i) )_1
 \because  v e_i \gcd で畳み込みすれば、その第  1 成分は  \gcd(i, a_j) = 1 なる j の個数となる。
これを式変形すると
 = e_1^T(M^{-1}(Mv \circ Me_i) )
 = (e_1^T M^{-1})(Mv \circ Me_i)
 = ( (M^T)^{-1}e_1)^T (Mv \circ Me_i)
 = ( (M^T)^{-1}e_1 \circ Mv)^T(Me_i) \quad \because \  x^T(y \circ z) = (x \circ y)^T z
 = ( ( (M^T)^{-1}e_1 \circ Mv)^T M)e_i
よって  i に依存しない部分を新しく文字で置いて
 u := ( (M^T)^{-1}e_1 \circ Mv)^T M
とすれば、求める値は
 ue_i = u_i
となることが分かった。
すなわち  u の各成分を出力すればよい。

 M 及びその転置や逆行列とベクトルとの積を  \Theta(N \log (\log (N) ) ) で計算するアルゴリズムを用いれば、この問題を解くことが出来る。

実装例

Submission #9522125 - 「みんなのプロコン 2018」決勝
分かりやすさのため  \Theta(A \log(A) ) の実装になっています。
 Mv : {\rm multiple\_zeta}(v)
 vM : {\rm divisor\_zeta}(v)
 (M^T)^{-1}v : {\rm divisor\_mobius}(v)

列の連結/全要素列挙をする永続データ構造

某に載せるほどか...? 載せることにしたらこっちは消えます
以下の操作を実現する永続データ構造が存在する

  •  make ( x )
    単一の要素  x からなる列を作成する
    時間/空間計算量  \Theta ( 1 )
  •  merge ( x , y )
     x , y を連結した列を作成する
    時間/空間計算量  \Theta ( 1 )
  •  enumerate ( x )
     x の全要素を順に列挙する
    時間計算量  \Theta ( | x |  )

アルゴリズム

各要素を葉に持つ二分木 (非平衡) を永続化すればよい。
 merge( x , y ) x を左子、 y を右子に持つノードを作る。
 enumerate は DFS で木を走査する。

f:id:noshi91:20191221000832j:plain