2020年11月15日15時58分44秒に更新されたバージョンを表示しています.
最新のページはこちらをご覧ください.
cLay概要(version 20201115-2)
概要
C++っぽい言語をC++言語に変換します.
無駄にライブラリを張るのがめんどくさい,鬱陶しい,ライブラリを整備していても綺麗に書けない処理があるなどを解消したいというのが目的.目的の達成はそのうち….
blogにコード載せる時にライブラリとかincludeとかが長ったらしい,のをなんとかしたいというのも動機の1つです.
#include<bits/stdc++.h>およびgetchar_unlocked,putchar_unlockedなどを使用しているので,それが動かない環境だと動かないし,動かないコードが出てきます.
(その場合は,フラグの欄を見て下さい)
コードの公開ページはこちら.C++っぽいコードを標準入力から食わせるとC++のコードが標準出力から出てきます.
細かいバージョンアップの際には,ドキュメント(本ページ)は更新されないかもしれません.
過去のバージョンの情報は,そのときの最新のドキュメントにリンクを張っていますので,ここに情報がない場合は,最新のバージョンのページも参照してください.
未対応なもの
重要度が高い順に,下線付き太字,太字,普通です.重要度が低いものはそもそも対応させる気がないものが多いです.
この欄ではC++ではできるけど…というもの主にリストアップ.
- 構造体 [現在暫定対応]・共用体・クラス [現在暫定対応]・enum
- typedef,defineなどで型を定義するとやばい.他にも括弧使わないと表せない複雑な型(int (*hoge)[10];とか)はダメ
- キャストとかもやばいかも
- 関数を含む式の計算結果の型の推定がうまくいかないはず
- do-while文,最近のC言語/C++の仕様
- invalidなコードを食わせると多分落ちる
- validだけど不自然な書き方とか,他にも色々.書ききれない
制限
以下の単語は変数名などで使うとやばい.
基本的にバージョンアップするときは互換性を保ちたいけど,此処の欄は普通に増やす.
chmax, chmin,
ll, ull, mint, segtree, combination_mint,
rd, reader,
wt, wtLn, wtSp, wtN, wtF, wt_L, writer, writerLn, writerSp, writerN, writerF,
min, max, argmin, argmax, argminL, argmaxL,
gcd, GCD, lcm, LCM, sum, SUM, mul, MUL,
sortA,
reduceFraction, runLength,
wmem, memarr,
malloc1d, malloc2d, free1d, free2d, walloc1d, walloc2d,
int_inf, ll_inf, double_inf, MD, PI
intは32bit,longは32bitか64bit,long longは64bitだと思って実装しています.違うとまずい場合があるかも.
色々
最初に
#include<bits/stdc++.h>
using namespace std;
が強制的に挿入されます.
#includeを書く必要はないですが,GCC以外で動かなくなる.
int main()は省略可能です.
いきなり関数の中じゃなくて{で始まるブロックが最初に出てきたときにint main()が補われます.
int main()で補われたブロック内の最後がreturn文でない場合は,return 0;が最後に補われます.
変数宣言や三項演算子 ? : を使っていない部分での,は(適切に{}を補った上で);に置き換えられます.
その結果,for(;;) i++, break; などと書くと for(;;){ i++; break; } に展開されます.
フラグ
途中で(プログラムの先頭が推奨)
//hogehoge
という形式でフラグを立てておくと色々な環境に対応させるために出力されるコードが変更されます.
- //no-unlocked : fread_unlocked, fwrite_unlocked, getchar_unlocked, putchar_unlocked が fread, fwrite, getchar, putchar に置き換わります.
- //no-fread : rd() において,fread_unlocked() を使わず getchar_unlocked() を使用します.scanf, cin などが現れたら自動的にこのフラグが指定されます.
- //no-fwrite : wt() において,fwrite_unlocked() を使わず putchar_unlocked() を使用します.printf, cout, puts が現れたら自動的にこのフラグを指定されます.このフラグを指定しないと,まとめて出力されるので,デバッグ等の理由で逐次出力したい場合は,このフラグを指定してください.
- //interactive : インタラクティブ形式の問題に対応するもの(現在codeforcesのみで動くことを確認).fread() や fwrite() を使うのをやめ,unlockedでない getchar(), putchar() を使って入出力するようにします.また,改行を出力したとき自動的に fflush(stdout) します.readerFile(), writerFile() は使用できなくなります.
- //no-gcd(), //no-GCD() : それぞれ gcd(), GCD() の機能を無効化します
- //no-lcm(), //no-LCM() : それぞれ lcm(), LCM() の機能を無効化します
- //no-min(), //no-MIN() : それぞれ min(), MIN() の機能を無効化します(min[]()の機能は有効のままです)
- //no-max(), //no-MAX() : それぞれ max(), MAX() の機能を無効化します(max[]()の機能は有効のままです)
- //no-sum(), //no-SUM() : それぞれ sum(), SUM() の機能を無効化します
- //no-mul(), //no-MUL() : それぞれ mul(), MUL() の機能を無効化します
またmint構造体など使用すると挿入される関数や構造体を挿入されなくするフラグは以下の通りです.
例えば,//no-insert-mintを指定するとmint型が使えなくなる代わりに,mintという変数や関数などを自由に宣言することができるようになります.
- //no-insert-stdc : #include<bits/stdc++.h>
- //no-insert-namespace : using namespace std;
- //no-insert-define_MD : #define MD 1000000007
- //no-insert-define_PI : #define PI 3.14159265358979323846
- //no-insert-workmemory
- //no-insert-workmemory_init
- //no-insert-walloc1d
- …
型
ll は long long に,ull は unsigned long long に置き換えられます.
VI は vector<int> に,VII は vector<vector<int>> に,VIII は vector<vector<vector<int>>> に置き換えられます.
mint型が使えます.
mod MDで計算します(MDは#define MD hogeで定義しなければデフォルトでは10^9+7が使用されます.MDは奇素数でなければ上手く動かない可能性があります.定数の項も参照のこと).
四則演算(割り算も)は使えます.int型やll型などと計算すると結果はmint型になります.
mint型の法の設定などはmain関数の変数宣言後に行うので,変数宣言時に代入などすると上手く動かないので,変数宣言時に代入するのは避けたほうが良いです.
mint x; x = 1; x /= 2; wt(x); で 500000004 が表示されます.
また,べき乗 pw が使えます.
mint x, p; x = 2; p = x.pw(100); で p に $2^{100} \bmod (10^9+7)$ が代入されます.
mint x; x.setmod(1000000009); とすることで,途中で mod の値を変えることができます.
mintの詳しい仕様はそのうち書きます.
モンゴメリ乗算を使用しないmodint型もあります.
MDが素数でなくても,割り算をしなければmodint型は動くと思います.
mint型とmodint型はほぼ同じ使用方法です.
途中で mod を変更できないバージョンとして1文字目を大文字にした Mint, Modint があります.
こっちのほうが高速なはず.初期化も必要ないので,変数宣言時に代入しても大丈夫なはず.
mint, modint, Mint, Modint はコンストラクタで 0 に設定されるはずです.
入出力1
unlocked シリーズが使えない場合は,//no-unlocked フラグを指定してください.unlocked じゃないものを利用します.
rd(i, j, k)で標準入力からi,j,kが読み込まれます.scanf("%d%d%d",&i,&j,&k);などと似た感じですが,スペース区切りじゃなく,数字以外で区切られます.
rd(i, j(n), k)はrd(i, j[0], j[1], ..., j[n-1], k)に置き換わります(左から処理するのでrd(n, A(n))とか可能です).
また,rd(N, (A,B)(N)) は rd(N, A[0], B[0], A[1], B[1], ..., A[N-1], B[N-1]) に置き換わります.グラフのエッジの入力とかに.
2次元の入力は rd(arr(3,3)) で rd(arr[0][0], arr[0][1], arr[0][2], arr[1][0], arr[1][1], arr[1][2], arr[2][0], arr[2][1], arr[2][2]) になります.3次元以上も同様.
rdの代わりにreaderでもOK.
rdは今のところint型,unsigned型,ll型,ull型,double型(指数表記は対応していない),mint型,char型(1文字),char型の配列(文字列),string型,vector<T>,Matrix<T>,Permutation,IntMap,Grid1d<T>,Grid2d<T>にのみに対応.ただし,Tは対応している型でなければならない.他の型もそのうち対応.
また,char S[1000]; int N; rd(S@N);で文字列 S を読み込むと同時に文字列の長さを N に代入します.複数の文字列の場合は char S[10][1000]; int N[10]; rd(S@N(10)); とS@Nはひとかたまりで扱います.
char型1文字の入力の場合も,空白などは読み飛ばして,次の空白でない文字を読み込みます.
rd(i--) は rd(i); i--; になります.++もできます.2減らすなら rd(i----) です.
rd((A--)(N)) は配列AのN個の要素すべてが1減ります.カッコは必須です.rd(A--(N)) は書けません.
rd((A--,B--,C)(N)) は配列A,Bの要素は1減り,Cの要素はそのままです.
1-originから0-originに直しながら入力するときなど.
A[2], B[4], A[3], B[5], A[4], B[6] に読み込みたい場合は rd(((A+2),(B+4))(3)); で可能です.
A[2], A[3], A[4] に読み込む場合は rd(((A+2))(3)); で可能です(かっこが多すぎる気がしますが,現状これだけ必要です…:(A+2)[0], (A+2)[1], (A+2)[2]でアクセスします).
int len; char buf[100];
len = rdLine(buf); で1行の読み込みを行います.
正確には,\n か EOF に出くわすまでbufに入れていきます(\nは入らない)が,\rだけは無視されます.
lenには基本的に読み込んだ文字数を返しますが,1文字も読み込まずEOFに出くわした場合のみ-1を返します.
bufの最後の文字の次に \0 を入れて返します.
変数に代入しないものとして,以下の関数は次の入力をその型の値として戻り値で返します.
- inline int rd_int(void)
- inline ll rd_ll(void)
- inline string rd_string(void)
wt(i, j, k)でスペース区切りでi, j, kを標準出力に吐き出し,改行します.
wt(i, j(n), k)はwt(i, j[0], j[1], ..., j[n-1], k)に置き換わります.
また,wt(N, (A,B)(N)) は wt(N, A[0], B[0], A[1], B[1], ..., A[N-1], B[N-1]) に置き換わります.式の一部がこの形でも同様に置き換わります(以下の例を参照).
最後に改行する代わりにスペースを出力する場合はwtの代わりにwtSpを,改行区切りで出力する場合はwtLnを使います.また区切りに何も出力しない場合はwtNを使います.
wtの代わりにwriter,wtSpの代わりにwriterSp,wtLnの代わりにwriterLnでもOK.
wtは今のところint型,ll型,double型,mint型,char型の配列(文字列),string型のみに対応.他の型もそのうち対応.
(ただし,string型は参照渡し.変数に代入せずにそのまま渡すときは .c_str() をつけるとchar型の配列で出力できると思われます)
wtFは文字列を出力しますが,変数名を{}で囲むと,その変数の値に置換されて出力されます.文字列中で{または}を出力したい場合はエスケープして\{または\}と書きます.
関数 wt(f(a)) は,fが変数として認識されてないと f(0), f(1), ..., f(a-1) に展開されませんが,fが変数として認識されていると展開してしまいます(特に構造体を作ってその変数fを定義している場合).
配列の展開しない場合,Wt(f(a)) や Writer(f(a)) のように W を大文字にしてください.
double型の変数を wt() で出力する場合,デフォルトでは小数点以下15桁表示します.
10桁表示したい場合は,事前に writerDigit_double(10); としてください.
また,現在の表示桁数を取得する場合は,writerDigit_double(); で取得できます.
中身が NaN, Inf などの変数を表示させた場合,現状は Err と表示されます.
2次元以上の配列の出力は,wtLn など区切り文字を明示的に指定したものを使うと各要素がそれで区切られます.
wt を使った場合,配列単体を出力する場合,および,出力の最後が配列の場合は,配列の最後のインデックスに対して1行に出力されます.
wt(arr(2,3)) の場合は,
arr[0][0] arr[0][1] arr[0][2]
arr[1][0] arr[1][1] arr[1][2]
となり
wt(arr(2,2,3)) の場合は
arr[0][0][0] arr[0][0][1] arr[0][0][2]
arr[0][1][0] arr[0][1][1] arr[0][1][2]
arr[1][0][0] arr[1][0][1] arr[1][0][2]
arr[1][1][0] arr[1][1][1] arr[1][1][2]
と出力されます.最後の要素でない場合は,1行に出力されます.
wt(arr(2,3), "hoge", 100) の場合は
arr[0][0] arr[0][1] arr[0][2] arr[1][0] arr[1][1] arr[1][2] hoge 100
です.
また,出力に関してオプションで B=n と指定すると整数系のものは n 進数で出力されます.
例えば,wt[B=3]("hello", 10); は hello 101 と出力されます(文字列に関しては基数は無視されます).
62進数まで指定可能で,文字は 01...9ABC...Zabc...z の順番です.
例:
int N, A[10], B[10], res[10];
rd(N, (A,B)(N));
wt(N, 2*res(N)+1);
は以下と同じ意味になります.
int i, N, A[10], B[10], res[10];
scanf("%d", &N);
for(i=0;i<N;i++) scanf("%d%d", A+i, B+i);
printf("%d ", N);
for(i=0;i<N;i++) printf("%d%c", 2*res[i]+1, i==N-1?'\n':' ');
また
int T = 0, res = 10;
wtF("\{Case {++T}: {res}\}\n");
は以下と同じ意味になります.
int T = 0, res = 10;
printf("{Case %d: %d}\n", ++T, res);
また
int N, L[10];
char S[10][1000];
rd(N, S@L(N));
は以下と同じ意味になります.
int i, N, L[10];
char S[10][1000];
scanf("%d",&N);
for(i=0;i<N;i++){
scanf("%s",S[i]);
L[i] = strlen(S[i]);
}
標準入出力ではない場合は,readerFile(),writerFile()を利用して入出力先を指定します.
ファイル名(勝手にfopen, fcloseされる)かファイルポインタを引数に指定します.
int i, j, k;
rd(i); // stdinから読み込み
readerFile("input.txt");
rd(j); // input.txtから読み込み
readerFile();
rd(k); // stdinから読み込み
wt(i); // stdoutに書き込み
writerFile("output.txt");
wt(j); // output.txtに書き込み
writerFile("log.txt", "a");
wt(k); // log.txtに追加で書き込み "a"モードで開く
writerFile(stderr);
wt("hoge"); // stderrに書き込み
入出力2
変数を宣言した際に変数名の前に@をつけると,その時点でrd()を利用して入力します.
現状,配列などは対応していないし,読み込むついでにインクリメント,デクリメントする(++, --)など対応していません.
{
int @A, @B, res = abs(A-B)/+10;
wt(res);
}
は
{
int A;
rd(A);
int B;
rd(B);
int res = abs(A-B)/+10;
wt(res);
}
となります.
ループ
rep(n) は int hoge; for(hoge=0;hoge<n;hoge++) に置き換えられます(hogeは適当な変数名).
rep(i,n) は for(i=0;i<(n);i++) に置き換えられます.
rep(i,a,b) は for(i=(a);i<(b);i++) に置き換えられます.
rep(i,a,b,s) は for(i=(a);i<(b);i+=(s)) に置き換えられます.
REP(n) は int hoge, piyo = n; for(hoge=0;hoge<piyo;hoge++) に置き換えられます(hoge, piyoは適当な変数名).
REP(i,n) は int piyo = n; for(i=0;i<piyo;i++) に置き換えられます(piyoは適当な変数名).
REP(i,a,b) は int piyo = b; for(i=(a);i<piyo;i++) に置き換えられます(piyoは適当な変数名).
REP(i,a,b,s) は int piyo = b, huga = s; for(i=(a);i<piyo;i+=huga) に置き換えられます(piyoは適当な変数名).
また,この際,変数iが宣言されていなかった場合,勝手にint型の変数として宣言されます.
rep では終了条件の変数・式 (n, b) が複数回評価されたり,ループの中で書き換え可能ですが,REP はそうではないことに注意してください.
同じ範囲をループで逆順で回す場合は以下があります(sを指定した場合,必ずしも同じ範囲ではないかもしれない).
rrep(n) は int hoge; for(hoge=(n)-1;hoge>=0;hoge--) に置き換えられます(hogeは適当な変数名):使う意味はないです.
rrep(i,n) は for(i=(n)-1;i>=0;i--) に置き換えられます.
rrep(i,a,b) は for(i=(b)-1;i>=(0);i--) に置き換えられます.
rrep(i,a,b,s) は for(i=(b)-1;i>=(a);i-=(s)) に置き換えられます.
RREP(n) は int hoge, piyo = n; for(hoge=piyo-1;hoge>=0;hoge--) に置き換えられます(hoge, piyoは適当な変数名):使う意味はないです.
RREP(i,n) は int piyo = n; for(i=piyo-1;i>=0;i--) に置き換えられます(piyoは適当な変数名).
RREP(i,a,b) は int piyo = a; for(i=(b)-1;i>=piyo;i--) に置き換えられます(piyoは適当な変数名).
RREP(i,a,b,s) は int piyo = a, huga = s; for(i=(b)-1;i>=piyo;i-=huga) に置き換えられます(piyoは適当な変数名).
配列の中身を変数に代入して使用したい場合は,例えば rep[arr](i,n) hoge; および rep[arr](i,n){ hoge; } は
rep(tekitouna_namae, n){
auto &i = arr[tekitouna_namae];
hoge;
}
に展開されます.rep, REP, rrep, RREP それぞれ,引数の数も自由です.
また,インデックスも利用する場合,例えば rep[arr,ind](i,n) hoge; および rep[arr,ind](i,n){ hoge; } は
rep(ind, n){
auto &i = arr[ind];
hoge;
}
に展開されます.
変数 i は既に宣言されていても,ブロックの中で再び定義されます.
ループから抜ける break について,二重ループから抜ける break_break などがあります.
break_break は break した後,その直後に break したのと同じ挙動になるはずです.
3重ループを抜ける break_break_break や,一つ上のループに対して直接continueをする break_continue などがあります.
break_ を何回か繰り返して break または continue を最後に付けた文があり,break_A は break を行って,ループを抜けた直後に A を行う感じです.
また,括弧内((),[],{}のどれか)で2つ以上のドットで区切ると範囲を指定でき,勝手にループに展開されます.
ドットの数が等しいものが同じ変数だと思われて,同じドットの数のものは2回目以降に登場したときには区間の終わりは省略可能です.
(ドットの数が少ない方が内側のループになります.2次元配列のアクセス順で速度がかなり変わるので気をつけたほうが良い場合があります)
例えば,単位行列を作るコード,行列のコピーのコード,転置しながらコピーするコードは
int A[10][10], B[10][10], C[10][10];
A[0...9][0..9] = 0;
A[0..9][0..9] = 1;
B[0...9][0..9] = A[0...9][0..9];
C[0...9][0..9] = A[0..9][0...9];
のように書け,これは
int i, j, A[10][10], B[10][10];
for(i=0;i<=9;i++) for(j=0;j<=9;j++) A[i][j] = 0;
for(i=0;i<=9;i++) A[i][i] = 1;
for(i=0;i<=9;i++) for(j=0;j<=9;j++) B[i][j] = A[i][j];
for(i=0;i<=9;i++) for(j=0;j<=9;j++) C[i][j] = A[j][i];
と同値です(B[0...9][0..9] = A[0...9][0..9];はB[0...9][0..9] = A[0...][0..];と書くこともできます).
括弧で囲まれていなければならないことに注意して下さい.
A[0..9] = (0..9)
B[0..9] = 50 - (23..)
は
for(i=0;i<=9;i++) A[i] = i;
for(i=0;i<=9;i++) B[i] = 50 - (23 + i);
と同値ですが, A[0..9] = 0..9; などとは書けません.
文字列の中に2個以上ドットが並ぶとバグる実装なのでそのうちなんとかする.
特殊なループ(多重ループ)
- rep_perm(d,n) : 0 から n-1 の置換に対するループ.$0 \leq d[0], d[1], \ldots, d[n-1] < n$ かつ $d[i] \neq d[j]$.
- rep_scomb(d,n,m) : 0 から m-1 までの整数から順序を問わず n 個取り出す組合せのループ.$0 \leq d[0] < d[1] < \cdots < d[n-1] < m$.
- rep_mcomb(d,n,m) : 0 から m-1 までの整数から順序を問わず重複を許して n 個取り出す組合せのループ.$0 \leq d[0] \leq d[1] \leq \cdots \leq d[n-1] < m$.
- rep_sarr(d,n,m) : 0 から m-1 までの整数から順序を加味して n 個取り出す組合せのループ.$0 \leq d[0], d[1], \ldots, d[n-1] < n$ かつ $d[i] \neq d[j]$.多分低速なので注意.
- rep_marr(d,n,m) : 0 から m-1 までの整数から順序を加味して重複を許して n 個取り出す組合せのループ.$0 \leq d[0], d[1], \ldots, d[n-1] < n$.
たとえば,
int d[10], n = 10;
rep_perm(d,n){
hoge;
}
は
int d[10], n = 10;
for(int tmp = 0; tmp < n; tmp++){
d[tmp] = tmp;
}
do{
hoge;
} while(next_permutation(d, d+n));
に置換されます.
他のもだいたい同様で,辞書順最小のものからループします.
(重複を許さない組合せで n より m のほうが小さい場合(条件を満たす配列が存在しない場合)は,ループの中身は1回も実行されません)
このループに使用する,next_permutation() に相当する,条件を満たす配列で,辞書順で次のものを求める関数 scomb(), mcomb(), sarr(), marr() 等もあり以下のとおりです.
- int next_mcomb(int len, int arr[], int lim) : 長さ len の配列 arr[] は条件 hoge を満たすとする.その条件を満たす辞書順で次のものに変換し 1 を返す.辞書順で次のものがなければ,辞書順最小のものに変換し 0 を返す.
- int next_scomb(int len, int arr[], int lim) : 長さ len の配列 arr[] は条件 hoge を満たすとする.その条件を満たす辞書順で次のものに変換し 1 を返す.辞書順で次のものがなければ,辞書順最小のものに変換し 0 を返す.
- int next_marr(int len, int arr[], int lim) : 長さ len の配列 arr[] は条件 hoge を満たすとする.その条件を満たす辞書順で次のものに変換し 1 を返す.辞書順で次のものがなければ,辞書順最小のものに変換し 0 を返す.
- int next_sarr(int len, int arr[], int lim, void *mem = wmem) : 長さ len の配列 arr[] は条件 hoge を満たすとする.その条件を満たす辞書順で次のものに変換し 1 を返す.辞書順で次のものがなければ,辞書順最小のものに変換し 0 を返す.毎回 O(len+lim) 時間かかるので注意.
- template<class T> int next_sarr_s(int len, int arr[], int lim, T use[]) : 長さ len の配列 arr[] は条件 hoge を満たすとし,use[k] は arr[i] = k なる i が存在すれば 1,そうでなければ 0 を満たすとする.その条件を満たす辞書順で次のものに変換し 1 を返す.辞書順で次のものがなければ,辞書順最小のものに変換し 0 を返す.
hoge は rep_piyo の説明を参照.
演算子
昔のGCC拡張で存在した<?および>?演算子の同時に代入するバージョンのみ使えます.
つまり,a <?= b; は a = min(a, b); と,a >?= b; は a = max(a, b); と大体同じです.
代入しないバージョンは素直にminおよびmaxを使え.
正の整数に関する繰り上げの割り算演算子 /+ が使えます.
13 /+ 3 は 5 となります.
実際には a /+ b は (a+b-1) / b で定義され,正の整数でないと繰り上げ割り算になる保証はありません.
また,a+b-1 がオーバーフローする可能性があるので要注意です.
a = a /+ b; は a /+= b; と書くこともできます.
負などもありうる場合には以下の関数が使えます.
- template<class T, class S> inline T fDiv(T a, S b) : $\lfloor a/b \rfloor$ を返します.
- template<class T, class S> inline T cDiv(T a, S b) : $\lceil a/b \rceil$ を返します.
負の数を正の数で割った余りを非負の数で返す %% 演算子があります.
(-5) %% 2 は 1 になります.
演算子の優先順位が低いので,-5 %% 2 は-(5 %% 2) = -1になります.
a = a %% b; は a %%= b; と書くこともできます.
べき乗演算子 ** が使えます.
2 ** 3 は $2^3 = 8$ を表します.
点 (x1, y1) と (x2, y2) とのユークリッド距離は sqrt( (x1-x2)**2 + (y1-y2)**2 ) と簡潔に書けるようになります.
バイナリ法を使用します.
x ** y において,y は非負整数である必要があって,結果の型は x の型と同じになります(y が非負整数じゃない場合は標準の pow 関数などを使って下さい).
ただし,x, y ともに double 型のときは,標準の pow(x,y) が呼び出されます.
a = a ** b; は a **= b; と書くこともできます.
数値の後の掛け算の演算子*は省略できることがあります.
z = 2 2x+2(x+5); で z = 2 * 2 * x + 2 * (x+5); の意味になります.
他の解釈ができる場合は*を省略できません.
2u は 2*u ではなくunsigned 型の定数 2 で,3d1 は 3*10 = 30 を表す整数で 3 * d1 などにはなりません.
数値はアルファベットで終わっている場合は,*を(現状では)省略できません.
z = 2u x; は駄目です.
また,数値の後以外の*の省略は(現状では)できません.
例えば,x(x+y) や (2)(x+y) に * が補われることはありません.
また,単純に*が補われるだけなので,x=2 のときに 7%2x は 7%2*x=1*2=2 となることに注意してください(7%(2x)ではない).
等式,不等式の条件を続けるときに && を省略できます.
if(5 < x+y < 10) は if(5 < x+y && x+y < 10) に置き換わります.
if(i <= 5 < k <= 12 > j != m == n) は if(i <= 5 && 5 < k && k <= 12 && 12 > j && j != m && m == n) に置き換わります.
if(5 < x++ < 10) は if(5 < x++ && x++ < 10) に置き換わるので注意してください.
ただし,< hoge > の場合は,テンプレート絡みなどで使用することがあるので,この場合のみ展開されません.
(a1,a2,...,an) += (b1,b2,...,bn); または (a1,a2,...,an) += b; の形で複数の演算を一度に行います.
コンマ演算子をオーバーロードしていると変なことが起こるかもしれないので,この機能を無効化する場合は //no-arraylike-operations のフラグを指定してください.
具体的には,
(a,b) = (b,a+b);
は
{
auto tmp1 = b;
auto tmp2 = a+b;
a = tmp1;
b = tmp2;
}
と展開され,(a,b) += k; は
{
auto tmp = k;
a += k;
b += k;
}
と展開されます.
現状は,左辺は()で囲まれカンマ区切りで n 個 (n ≥ 2) の変数 (左辺値) がある場合,右辺は括弧で囲まれない 1 個の式か,括弧で囲まれた 1 個または n 個の式である必要があります.
また,演算子には = が含まれている必要があります.単項演算は現状非対応です.
a = b = 0 のとき,(a,b)+=(2,1); をやると a=2, b=1になりますが,C++ではa=0, b=1となります.結果が変わるので注意してください.
定数
int_inf は 1073709056 ($2^{30}-2^{15}$) に,
ll_inf は 4611686016279904256LL ($2^{62}-2^{31}$) に,
double_inf は 1e150 に置換されます.
int_infおよびll_infはそれぞれ2倍してちょっとした数を足してもint型,long long型でオーバーフローしないという値に設定しています.
また,double_infは2乗してもオーバーフローしないという値に設定しています.
また,MDは1000000007($10^9+7$)に,PIは3.14159265358979323846(円周率)に置換されます(厳密には最初に#define MD 1000000007などが追加されます).
MDとPIはコード中で #define MD 1000000009 などと言う記述が出てきたら置換されずそちらが優先されます.
また,double型の定数の書き方 1e-5 などと同様に,整数型の定数を 1d5 などと書けます.
$x$d$y$ で $\lfloor x \times 10^y \rfloor$ を表します.
$2^{31}$ 未満であれば int 型,$2^{31}$ 以上であれば ll 型,$2^{63}$ 以上であれば ull 型の定数になり,$2^{64}$ 以上の値だと上手く動きません.
$y$ は整数で $-30$ 以上 $30$ 以下でないと正しく動きません.
ワーキングメモリ
ワーキングメモリを必要な関数や機能などを使うと勝手にワーキングメモリを確保します.
void *wmem; に(デフォルトで)96,000,000バイトのメモリが確保されます.
サイズを変更する場合は,どこか(の行頭)で,
//working_memory=500MB
などと書くと500*1024*1024バイトのメモリになります.
//working_memory=500m
//working_memory=524288000
も同じ意味です.Bは省略可能,K,M,Gが接頭語として使えます.
行列
行列は Matrix<mint> m; などで定義します.
m[0][1] で0行1列の成分にアクセスできます(行番号,列番号は0から始まる).
行列の足し算,引き算,かけ算は+,-,*でできます.べき乗演算子 ** なども使えます.定義できない演算をすると0行0列の行列になります.
int, ll, doubleの定数倍も可能です.
m = 1; などと定数を代入すると対角成分が1に,それ以外は0になります.
以下は Matrix<T> でに関してです.
コンストラクタ
- Matrix(): 0行0列の行列
- Matrix(const int rr, const int cc): rr行cc列の行列.要素の初期化はされない.
- Matrix(const Matrix<T> &a)
その他
- void m.changeSize(const int rr, const int cc): 行列のサイズをrr行cc列に変更
置換
置換は Permutation p; などで定義します.
p[i] = j は第i要素を第j番目に飛ばす,という意味です.
i も j も 0-origin です.
現状 p ** n の計算は(他の型と同様に)バイナリ法です.
コンストラクタ
- Permutation(): 要素数0の置換
- Permutation(const int nn): 要素数nnの置換.メモリは確保するが初期化はしない.
- Permutation(const Permutation &a)
デストラクタ
演算子
- Permutation& operator=(const Permutation &a) : 恒等置換にします
- Permutation& operator=(const int a) : a=1 のとき,恒等置換にします.aが1でない場合は未定義です.
- Permutation& operator*=(const Permutation &a) : 置換 a, b に対して,a * b は置換 b を作用させた後,置換 a を作用させるのと同値な置換を返します.順番に注意(ワーキングメモリを使用します)
- Permutation operator*(const Permutation &a)
- bool operator==(const Permutation &a)
- inline int& operator[](const int a)
メンバ関数
- void p.changeSize(const int nn) : 置換のサイズを nn にします(メモリの確保をします,データは破壊されることがあります,nn次の置換).
- template<class T> void p.apply(T A[], T B[]) : 配列Aに置換を作用させて,その結果を配列Bに返します
- template<class T> void p.apply(T A[]) : 配列Aに置換を作用させて,その結果で配列Aを上書きします(ワーキングメモリを使用します)
- int p.cycle_len(int res[] = NULL) : 置換をサイクルに分解したとき,そのサイクルの長さからなる配列resを返します.戻り値はサイクルの数です.
- void p.cycle_len_EachElement(int res[]) : res[i] に i を含むサイクルの長さを代入します.
- template<class T> inline T p.getIndex(void *mem = wmem) : サイズ n の中で,辞書順で何番目の置換なのかを返す.Fenwick treeを利用してO(n log n)で求める.int型で求める場合は p.getIndex<int>() で(ただしすぐオーバーフローする).
{
int A[10] = {5,2,8,9,1,1,100,800,9,10};
int B[10];
int sz;
int pp1[10] = {1,2,3,4,5,6,7,8,9,0};
int pp2[10] = {1,0,3,2,5,4,7,6,9,8};
Permutation p1, p2, m, id;
p1.changeSize(10);
p2.changeSize(10);
rep(i,10) p1[i] = pp1[i];
rep(i,10) p2[i] = pp2[i];
wt(A(10)); // 5 2 8 9 1 1 100 800 9 10
p1.apply(A, B);
wt(B(10)); // 10 5 2 8 9 1 1 100 800 9
p2.apply(A, B);
wt(B(10)); // 2 5 9 8 1 1 800 100 10 9
m = p1 ** 13;
m.apply(A, B);
wt(B(10)); // 800 9 10 5 2 8 9 1 1 100
m = p1 * p2;
m.apply(A, B);
wt(B(10)); // 9 2 5 9 8 1 1 800 100 10
p2.apply(A);
p1.apply(A);
wt(A(10)); // 9 2 5 9 8 1 1 800 100 10
id.changeSize(10);
id = 1; // identity
rep(i,100){
m = p1 ** i;
if(m==id) wt(i); // i = 0, 10, 20, ..., 90
}
rep(i,100){
m = p1 ** i;
if(m==p1) wt(i); // i = 1, 11, 21, ..., 91
}
sz = p1.cycle_len(B);
wt(B(sz)); // 10
sz = p2.cycle_len(B);
wt(B(sz)); // 2 2 2 2 2
sz = id.cycle_len(B);
wt(B(sz)); // 1 1 1 1 1 1 1 1 1 1
}
IntMap
{0,1,...,n-1} から {0,1,...,n-1} の写像は IntMap f; などで定義します.
f[i] = j は第i要素を第j番目に飛ばす,という意味です.
コンストラクタ
- IntMap() : n=0
- IntMap(const int nn) : 要素数n=nnとする.メモリは確保するが初期化はしない.
デストラクタ
演算子
- inline int& operator[](const int a) : これを使って写像を定義する
メンバ関数
- void f.changeSize(const int nn) : n = nn にします(メモリの確保をします.f[i]の値や,過去計算した前計算の結果は破棄されます).
- int f.calcCycle(void) : サイクルを全て求める.戻り値はサイクルの数.サイクルの情報は変数のいくつかに代入される.O(n)時間.サイクルは x[0], x[1], x[2], ..., x[k] = x[0] で x[i+1] = f[x[i]] なるもの.
- void f.calcNext(int recalc = 1) : s回写像を適応した結果,f^s[x] = f[f[...f[x]...]] を計算するための前処理をする.ダブリングとサイクルの計算を行う.calcCycleを行っていて,fの内容を破壊していないならrecalc=0で呼び出せばサイクルは再計算しない.O(n log n)時間.
- template<class T> int f.getNext(int x, T s) : s回写像を適応した結果,f^s[x] = f[f[...f[x]...]] を返す.calcNext()を呼び出していない場合,初回に計算する.基本的にはO(log n)時間.Tは整数を表す型.
変数
- int f.numCycle : サイクルの数.calcCycle()で計算される.
- int f.cycleLen[i] : i番目のサイクルの要素数.calcCycle()で計算される.
- int f.cycle[i][j] : i番目のサイクルのj番目の要素.calcCycle()で計算される.
- int f.int2c[i] : 整数iが属するサイクルの番号.サイクルに属さないなら-1.calcCycle()で計算される.
- int f.int2cInd[i] : 整数iが属するサイクルの要素番号(cycle[int2c[i]][int2cInt[i]] = i).サイクルに属さないなら-1.calcCycle()で計算される.
変数(多分使わない)
- int f.n
- int f.mem : メモリ確保した配列サイズ
- int f.nx[i][j] : ダブリングの配列.jを2^i回飛ばした先を格納
- int f.calc_nx : nx配列を計算しているならば1,していなければ0
- int f.logn : nx配列の1次元目のサイズ
Fenwick Tree (Binary Indexed Tree, BIT)
(1次元の)Fenwick treeが使えます.
fenwick<int> t; などで定義します.この場合は各要素や和などを求める時に全てint型で計算されます.
扱う配列の長さの最大値を maxN として,t.malloc(maxN); または t.walloc(maxN); でメモリを確保します.
その後,配列の長さが N の場合に初期化(全要素を0にする)場合,t.init(N); とします.
メモリ確保とともに,確保したメモリ分の長さを初期化する場合は,t.malloc(N, 1); または t.walloc(N, 1); です.
関数などは以下の通り(fenwick<T> t; で宣言したとします.また,要素数を $N$ として,0番目の要素から$N$-1番目の要素まで存在します):
- void t.malloc(int mem) : mem 要素分のメモリを確保します.malloc使用.
- void t.malloc(int mem, int fg) : mem 要素分のメモリを確保し,fg が 0 でなければ mem 要素分 0 に初期化します.malloc使用.
- void t.walloc(int mem, void **workMemory = &wmem) : mem 要素分のメモリを確保します.ワーキングメモリ使用.
- void t.walloc(int mem, int fg, void **workMemory = &wmem) : mem 要素分のメモリを確保し,fg が 0 でなければ mem 要素分 0 に初期化します.ワーキングメモリ使用.
- void t.add(int a, T val) : a 番目の要素の値に val を加えます.$O(\log N)$ 時間.
- T t.get(int a): 0番目の要素,1番目の要素,…,a番目の要素の和を返します.$O(\log N)$ 時間.
- T t.range(int a, int b): a番目の要素,a+1番目の要素,…,b番目の要素の和を返します.$O(\log N)$ 時間.
- int t.kth(T v): 各要素は全て非負のとき,t.get(a)がvを超える最小のaを求めます.下の例も参照.$O((\log N)^2)$ 時間,現在は良くない実装.
区間は閉区間 [a,b] の形で指定することに注意して下さい.
malloc()を使った場合のメモリの開放は t.free(); です.
{
int d[10] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
int arr[20];
int i, s;
fenwick<int> t;
t.walloc(10);
t.init(10);
rep(i,10) t.add(i,d[i]);
s = t.get(3);
wt(s); // d[0]+d[1]+d[2]+d[3] = 3+1+4+1 = 9
s = t.range(5,7);
wt(s); // d[5]+d[6]+d[7] = 9+2+6 = 17
rep(i,20) arr[i] = t.kth(i);
wt(arr(20)); // 0がd[0]個,1がd[1]個,…からなる配列のk番目の要素
// 0 0 0 1 2 2 2 2 3 4 4 4 4 4 5 5 5 5 5 5
}
バイナリヒープ(priority_queueとほぼ同様)
Heap<T> h; もしくは Heap_max<T> hm; で宣言する.
Heapは通常の最小値を取り出せるやつ,Heap_maxは最大値を取り出せるやつ.
大体の操作は $O(\log size)$.
Heap<T> h; について:
- int h.size : ヒープに入っている要素数.
- void h.malloc(const int N) : mallocを使ってN要素分を格納できるだけのメモリを確保.ヒープの中を空に設定する.
- void h.walloc(const int N, void **mem = &wmem) : ワーキングメモリを使ってN要素分を格納できるだけのメモリを確保.ヒープの中を空に設定する.
- void h.free() : mallocで確保したメモリを解放します.
- void h.init() : ヒープを空にします(h.size = 0).
- T h.top() : ヒープの中にある最小値を返します.
- T h.pop() : ヒープの中にある最小値を削除し,その値を返します.
- T h.push(const T x) : ヒープの中にxを入れます.戻り値はxの値そのものを返します.
Heap_max<T> hm; について:
- int hm.size : ヒープに入っている要素数.
- void hm.malloc(const int N) : mallocを使ってN要素分を格納できるだけのメモリを確保.ヒープの中を空に設定する.
- void hm.walloc(const int N, void **mem = &wmem) : ワーキングメモリを使ってN要素分を格納できるだけのメモリを確保.ヒープの中を空に設定する.
- void hm.free() : mallocで確保したメモリを解放します.
- void hm.init() : ヒープを空にします(h.size = 0).
- T hm.top() : ヒープの中にある最大値を返します.
- T hm.pop() : ヒープの中にある最大値を削除し,その値を返します.
- T hm.push(const T x) : ヒープの中にxを入れます.戻り値はxの値そのものを返します.
バイナリヒープ(固定長配列用)
LHeap<int> などで宣言.
$N$ 要素の配列 $A_k = \verb|val[k]|$ を考える.
ヒープにある要素を入れたり値を変えたり($\verb|change|$),ヒープの中に入っている最小の要素を取り出して取り出した要素の番号を返したり($\verb|pop|$)する.
大体の操作の計算時間 $O(\log N)$.
よく使いそうなもの:
- $\verb|malloc(int N)|$:$N$ 要素分のメモリを確保
- $\verb|malloc(int N, int ini)|$:$N$ 要素分のメモリを確保し,iniが0でなければ init(N) もやる
- $\verb|walloc(int N, void **mem=&wmem)|$:$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返す
- $\verb|walloc(int N, int ini, void **mem=&wmem)|$:$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返し,iniが0でなければ init(N) もやる
- $\verb|free(void)|$:$\verb|malloc(int N)|$で確保したメモリの解放
- $\verb|init(int N)|$:初期化.今後 $N$ 要素の配列を使う.ヒープの中は空.
- $\verb|change(int n, T v)|$:$A_k = v$ に変更する.ヒープに入っていない要素ならヒープに突っ込み,入っている要素だったら,適切な市に移動する.
- $\verb|pop(void)|$:ヒープに入っている最小の要素のインデックスを返し,その要素を消す.最小が $A_k$ なら $k$ を返す.
- $\verb|int size|$:ヒープに入っている要素の数.
- $\verb|int hp[]|$:$\verb|hp[k]|$ でヒープに入っている $k$ 番目の要素のインデックス.$\verb|pop()|$ で取り出される要素のインデックスは $\verb|hp[0]|$.
- $\verb|T val[]|$:$A_k$ の値 $\verb|val[k]|$.
そうではないもの:
- $\verb|int place[]|$:$\verb|place[k]|$ は $A_k$ がヒープに入っているならその位置,入っていないなら $-1$.
int i;
double v;
LHeap<double> hp;
hp.walloc(10);
hp.init(10);
hp.change(0, 100.0);
hp.change(1, 200.0);
hp.change(8, 80.0);
hp.change(1, -200.0);
hp.change(8, 150.0);
while(hp.size){
i = hp.pop(); // 1 -200.000000000000000
v = hp.val[i]; // 0 100.000000000000000
wt(i,v); // 8 150.000000000000000
}
バイナリヒープ(ダイクストラ用)
DijkstraHeap<int> などで宣言.
バイナリヒープ(固定長配列用)のダイクストラ用にチューニングしたもの.
$\verb|pop|$ した時に $\verb|visited[]|$ のフラグを立てる,更新時に訪れたものや長くなったものは更新しないなどを関数の中で判定する.
よく使いそうなもの:
- $\verb|malloc(int N)|$:$N$ 要素分のメモリを確保
- $\verb|walloc(int N, void *mem)|$:$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返す
- $\verb|free(void)|$:$\verb|malloc(int N)|$で確保したメモリの解放
- $\verb|init(int N)|$:初期化.今後 $N$ 要素の配列を使う.ヒープの中は空
- $\verb|change(int n, T v)|$:$A_k$ を既に処理していないなら,$A_k = \min(A_k, v)$ に変更する.ヒープに入っていない要素ならヒープに突っ込み,入っている要素だったら,適切な市に移動する
- $\verb|pop(void)|$:ヒープに入っている最小の要素のインデックスを返し,その要素を消す.最小が $A_k$ なら $k$ を返す
- $\verb|int size|$:ヒープに入っている要素の数
- $\verb|int hp[]|$:$\verb|hp[k]|$ でヒープに入っている $k$ 番目の要素のインデックス.$\verb|pop()|$ で取り出される要素のインデックスは $\verb|hp[0]|$
- $\verb|T val[]|$:$A_k$ の値 $\verb|val[k]|$
- $\verb|char visited[]|$:$A_k$ を始点とする枝たちを処理したなら $1$,そうでないなら $0$
そうではないもの:
- $\verb|int place[]|$:$\verb|place[k]|$ は $A_k$ がヒープに入っているならその位置,入っていないなら $-1$.
int i;
double v;
DijkstraHeap<double> hp;
hp.walloc(10);
hp.init(10);
hp.change(0, 100.0);
hp.change(1, 200.0);
hp.change(8, 80.0);
hp.change(1, -200.0);
hp.change(8, 150.0);
while(hp.size){
i = hp.pop(); // 1 -200.000000000000000
v = hp.val[i]; // 8 80.000000000000000
wt(i,v); // 0 100.000000000000000
}
セグメントツリー(標準的なの?)
(1次元の)セグメントツリーが使えます.
segtree<int> t; などで定義します.この場合は各要素や和などを求める時に全てint型で計算されます.
1回のみ使用する場合は,配列の長さを N として t.malloc(N, 1); とすると,配列の要素が全て 0 で初期化され,セグメントツリーが使えるようになります.
何回も使いまわす可能性がある場合は,配列の長さの最大値を maxN として,t.malloc(maxN); でメモリを確保します.
その後,配列の長さが N の場合にセグメントツリーを使えるようにするためには,t.setN(N); で使えるようになります.
mallocの代わりにwallocでワーキングメモリを使ってメモリを確保します.
0以外で初期化したい場合は,t.setN(N,0,0);(各要素は全く初期化されない),または,t.setN(N,1,0); (各要素は一応0で初期化される)としておき,
t[hoge] = 要素hogeの初期値;
と適当に初期化した後,t.build();とすれば使えるようになります.
segtree<T> t; とした場合,使える感じの関数は以下の通り(要素数を $N$ として,0番目の要素から$N$-1番目の要素まで存在します):
- void t.change(int a, int b, T val): a番目の要素,a+1番目の要素,…,b-1番目の要素の値を val にします.$O(\log N)$ 時間.
- void t.add(int a, int b, T val): a番目の要素,a+1番目の要素,…,b-1番目の要素の値に val を加えます.$O(\log N)$ 時間.
- T t.getSum(int a, int b): a番目の要素,a+1番目の要素,…,b-1番目の要素の和を返します.$O(\log N)$ 時間.
- pair<T, int> t.getMin(int a, int b): a番目の要素,a+1番目の要素,…,b-1番目の要素の (最小値, その最小値を実現する最初の要素の番号) を返します.$O(\log N)$ 時間.
- T t.getMinVal(int a, int b): a番目の要素,a+1番目の要素,…,b-1番目の要素の最小値を返します.$O(\log N)$ 時間.
- int t.getMinInd(int a, int b): a番目の要素,a+1番目の要素,…,b-1番目の要素の最小値を実現する最初の要素の番号を返します.$O(\log N)$ 時間.
区間は最初は含むが最後は含まない [a,b) の形で指定することに注意して下さい.
malloc()を使った場合のメモリの開放は t.free(); です.
セグメントツリー(亜種?)
初期化の仕方などは標準的な segtree と同じですが,機能が制限されていたり,変な機能がついているものです.
基本的に必要な機能のみがついているものを使うと,速くて省メモリのはずです.
基本的に構造体の名前が segtree_[変更に対する対応クエリの羅列]_[取得に関する対応クエリの羅列] という形式になっています(長い…).
例えば,
segtree_Point_SumMin<T> t;
だと,変更に関するクエリの
void t.add(int x, T val)
void t.change(int x, T val)
と取得に関するクエリの
T t.getSum(int a, int b)
pair<T, int> t.getMin(int a, int b)
T t.getMinVal(int a, int b)
int t.getMinInd(int a, int b)
が使用できます.
変更に関するクエリと構造体の名前に含まれていたら使用できるという文字列(,で区切られている場合どれかが含まれていれば使用可能)
- void t.change(int x, T val) : Point : x番目の要素を val に変更します
- void t.add(int x, T val) : Point : x番目の要素に val を足し込みます
- void t.change(int a, int b, T val) : Change : a番目の要素,a+1番目の要素,…,b-1番目の要素の値を val にします
- void t.add(int a, int b, T val) : Add, P1add : a番目の要素,a+1番目の要素,…,b-1番目の要素の値に val を加えます
- void t.p1add(int a, int b, T x1, T x0) : P1add : a番目の要素,a+1番目の要素,…,b-1番目の要素の値に x0, x0+x1, x0+2*x1, ..., x0+(b-a-1)*x_1 を加えます
取得に関するクエリと構造体の名前に含まれていたら使用できるという文字列(,で区切られている場合どれかが含まれていれば使用可能)
- T t.getSum(int a, int b) : Sum : a番目の要素,a+1番目の要素,…,b-1番目の要素の和を返します
- T t.getProd(int a, int b) : Prod : a番目の要素,a+1番目の要素,…,b-1番目の要素の積を返します
- pair<T, int> t.getMin(int a, int b) : Min : a番目の要素,a+1番目の要素,…,b-1番目の要素の (最小値, その最小値を実現する最初の要素の番号) を返します
- T t.getMinVal(int a, int b) : Minval, Min, Minval2 : a番目の要素,a+1番目の要素,…,b-1番目の要素の最小値を返します
- int t.getMinInd(int a, int b) : Min : a番目の要素,a+1番目の要素,…,b-1番目の要素の最小値を実現する最初の要素の番号を返します
- pair<T, T> t.getMinVal2(int a, int b) : Minval2 : a番目の要素,a+1番目の要素,…,b-1番目の要素の(最小値,2番目に小さい値)を返します.範囲内の要素数1でで2番目に小さい値が存在しない場合は numeric_limits<T>::max() が入っています.
- T t.getMaxVal(int a, int b) : Maxval : a番目の要素,a+1番目の要素,…,b-1番目の要素の最大値を返します
定義されている構造体
- segtree_Point_Minval<T> t
- segtree_Point_Maxval<T> t
- segtree_Point_Min<T> t
- segtree_Point_SumMin<T> t: 1点更新の標準的なsegtree
- segtree_Point_Prod<T> t
- segtree_Point_Minval2<T> t
- segtree_Add_Minval<T> t
- segtree_ChangeAdd_Sum<T> t
- segtree_ChangeP1add_Sum<T> t
その他の事項:
segtree_ChangeAdd_SumMin は通常の segtree を使ってください.
segtree_Point_Sum(1点更新で和を求めるだけのもの)はfenwickでできます(が,t.change(int x, T val) に対応するには値を配列などに保持し,差分を足してください).
セグメントツリー(1点更新の汎用的なの)
segtree_ph<T> t; で定義します.
初期化は通常のsegtreeと同じですが,0で初期化するように指定しても初期化されません(同じように書けるように引数は存在します).
res = f(a, b) を計算する関数
T segtree_ph_func(T a, T b){ hoge; }
をグローバルで定義しておきます.fは結合律 f(a,f(b,c)) = f(f(a,b),c) =: f(a,b,c) を満たすとします.
- void t.change(int x, T val) : x番目の要素を val に変更します
- T t.get(int a, int b) : f(a番目の要素,a+1番目の要素,…,b-1番目の要素) を返します.a = bの場合は何を返すか不定です.
例えば,T = int で,
void segtree_ph_func(int a, int b){ return a + b; }
を作っておくと,1点更新で,区間和を求めることができるsegtreeになります.
ただし,基本的には,汎用的に利用するため,構造体と関数を新しく定義して利用することを想定しています.
実装方法は以下も参考にしてください.
Codeforces Round #672 DIV2 C2問題 - Pokemon Army (hard version)
セグメントツリー(1点更新の汎用的なの,旧版)
segtree_phとの違いは segtree_p*_func の形式です.
segtree_pg<T> t; で定義します.
初期化は通常のsegtreeと同じですが,0で初期化するように指定しても初期化されません(同じように書けるように引数は存在します).
res = f(a, b) を計算する関数
void segtree_pg_func(T &res, T &a, T &b){ hoge; }
をグローバルで定義しておきます.fは結合律 f(a,f(b,c)) = f(f(a,b),c) =: f(a,b,c) を満たすとします.
- void t.change(int x, T val) : x番目の要素を val に変更します
- T t.get(int a, int b) : f(a番目の要素,a+1番目の要素,…,b-1番目の要素) を返します.a = bの場合は何を返すか不定です.
例えば,T = int で,
void segtree_pg_func(int &res, int a, int b){ res = a + b; }
を作っておくと,1点更新で,区間和を求めることができるsegtreeになります.
ただし,基本的には,汎用的に利用するため,構造体と関数を新しく定義して利用することを想定しています.
実装方法は以下も参考にしてください.
Codeforces Round #672 DIV2 C2問題 - Pokemon Army (hard version)
segtree_pg 名前の由来は p は1点更新を意味する point,g はグローバルに関数を定義する形式から名前をつけています.
セグメントツリー(区間更新の汎用的なの)
segtree_rh<SVAL, SFUN> t; で定義します.
初期化は通常のsegtreeと同じですが,0で初期化するように指定しても初期化されません(同じように書けるように引数は存在します).
res = h(a, b) を計算する関数
SVAL segtree_rh_merge(SVAL a, SVAL b){ hoge; }
をグローバルで定義しておきます.hは結合律 h(a,h(b,c)) = h(h(a,b),c) =: h(a,b,c) を満たすとします.
変更に関して,res = f(a) を計算する関数
SVAL segtree_rh_apply(SFUN f, SVAL a){ hoge; }
および,関数の合成 res(x) = f(g(x)) なる関数 res を計算する関数
SFUN segtree_rh_compose(SFUN &res, SFUN f, SFUN g){ hoge; }
をグローバルで定義しておきます.
- void t.change(int a, int b, SFUN f) : k=a,a+1,...,b-1に対して,k番目の要素 v[k] を f(v[k]) に変更します.
- T t.get(int a, int b) : h(a番目の要素,a+1番目の要素,…,b-1番目の要素) を返します.a = bの場合は何を返すか不定です.
AtCoder Libraryのlazy_segtreeとだいたい同じ機能です.実装方法は例えば以下を参考にしてください.
AtCoder Library Practice Contest K問題 - Range Affine Range Sum
AtCoder Library Practice Contest L問題 - Lazy Segment Tree
また,changeでは全て一定値で置き換え,getで最小値を取得の場合は,segtree_rh<ll, ll> で,
ll segtree_rh_apply(ll f, ll a){ return f; }
ll segtree_rh_merge(ll a, ll b){ return min(a, b); }
ll segtree_rh_compose(ll f, ll g){ return f; }
などでできますが,複雑になると構造体を作ったほうがわかりやすい気がします.
セグメントツリー(区間更新の汎用的なの,旧版)
segtree_rh との違いは segtree_rg_func の関数名,関数の形式の違いと,恒等写像を用意する必要があるところです.
segtree_rg<SVAL, SFUN> t; で定義します.
初期化は通常のsegtreeと同じですが,0で初期化するように指定しても初期化されません(同じように書けるように引数は存在します).
res = h(a, b) を計算する関数
void segtree_rg_func(SVAL &res, SVAL a, SVAL b){ hoge; }
をグローバルで定義しておきます.hは結合律 h(a,h(b,c)) = h(h(a,b),c) =: h(a,b,c) を満たすとします.
変更に関して,res = f(a) を計算する関数
void segtree_rg_func(SVAL &res, SFUN f, SVAL a){ hoge; }
および,関数の合成 res(x) = f(g(x)) なる関数 res を計算する関数
void segtree_rg_func(SFUN &res, SFUN f, SFUN g){ hoge; }
をグローバルで定義しておきます.
また,恒等写像を表す写像 res を返す
void segtree_rg_id(SFUN &res)
もグローバルで定義しておきます.
- void t.change(int a, int b, SFUN f) : k=a,a+1,...,b-1に対して,k番目の要素 v[k] を f(v[k]) に変更します.
- T t.get(int a, int b) : h(a番目の要素,a+1番目の要素,…,b-1番目の要素) を返します.a = bの場合は何を返すか不定です.
SVAL, SFUN を同じ型にすることは現状できません.
基本的には,汎用的に利用するため,構造体と関数を新しく定義して利用することを想定しています.
AtCoder Libraryのlazy_segtreeとだいたい同じ機能です.実装方法は例えば以下を参考にしてください.
AtCoder Library Practice Contest K問題 - Range Affine Range Sum
AtCoder Library Practice Contest L問題 - Lazy Segment Tree
また,changeでは全て一定値で置き換え,getで最小値を取得の場合は,segtree_rg<int, ll> で,
void segtree_rg_id(ll &res){ res = ll_inf; }
void segtree_rg_func(int &res, ll f, int a){ res = if[f==ll_inf, a, f]; }
void segtree_rg_func(int &res, int a, int b){ res = min(a, b); }
void segtree_rg_func(ll &res, ll f, ll g){ res = if[f==ll_inf, g, f]; }
などでできますが,構造体を作る余裕があるなら作ったほうがわかりやすい気がします.
segtree_rg 名前の由来は r は区間更新を意味する range,g はグローバルに関数を定義する形式から名前をつけています.
2次元領域木,2d range tree
2次元平面内の長方形領域に含まれる点の重みの和を求める.
rangeTree2d<S,T1,T2> t; で定義する.
Sは重みの型,T1は1次元目(x座標)の型,T2は2次元目(y座標)の型.
実装は現状,y座標の区間を扱うようなsegment treeで,各ノードには対応するx座標値をソートして保持,x座標の区間の重みを計算できるようにFenwick treeを保持.
領域量は O(N log N) でワーキングメモリを結構使うので注意.必要ならば,//working_memory=300m などしておくと良い.
- inline void build(int nn, T1 dd1[], T2 dd2[], S ww[] = NULL, void **mem = &wmem) : 点の数 nn,i番目の点の座標 (dd1[i], dd2[i]) で重みが ww[i].ww[] = NULL の場合は,全ての点の重みを 1 にします.時間計算量は O(N log N).
- inline int add(T1 x, T2 y, S v) : 点 (x,y) の重みを +v する.ただし,点 (x,y) が存在することを仮定している.2つ以上存在したら1個のみに +v する.時間計算量は O((log N)^2).
- inline S query(T1 x1, T1 x2, T2 y1, T2 y2) : x1 ≤ x < x2, y1 ≤ y < y2 なる点 (x,y) の重みの和を求める.時間計算量は O((log N)^2).
重みが一様(全て1)のバージョン rangeTree2d_nw もあります.nwはnon-weightedのつもり.
rangeTree2d<T1,T2> t; で定義する.
- inline void build(int nn, T1 dd1[], T2 dd2[], void **mem = &wmem)
- inline int query(T1 x1, T1 x2, T2 y1, T2 y2) : x1 ≤ x < x2, y1 ≤ y < y2 なる点 (x,y) の数を求める.
グラフ(構造体)
graph構造体を使えます.graph g; などで定義します.
- int g.N : ノード数
- int *g.es : g.es[i] はノード i から出ている枝の本数
- int **g.edge : g.edge[i][j] はノード i から出ている j 番目の枝が向かう先のノード番号
- void g.setEdge(int N, int M, int A[], int B[], void **mem=&wmem) : ノード数 N,枝数 M の無向グラフをセット(g.N, g.es, g.edgeを書き換え)します.(A[i], B[i])間に枝があります.枝は登場順に格納します.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- void g.setDirectEdge(int N, int M, int A[], int B[], void **mem=&wmem) : ノード数 N,枝数 M の有向グラフをセットします.A[i]からB[i]に向かう枝があります.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- void g.setEdgeRootedTree(int N, int M, int A[], int B[], int root=0, int reorder=0, int cnv[] = NULL, void **mem = &wmem) : ノード数 N,枝数 M の根付き木をセットします.A[i], B[i] の順番は問わず,親から子に向かう枝のみを設定します.reorder=1の場合は,節点の番号を根に近いから振り直します(番号が大きくなる方向にのみ枝があるようになります).cnv[i] = k は番号を付け替えた後のノード i はもともとのデータではノード k だったことを意味します.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- graph g.reverse(void **mem=&wmem) : 枝の向きを逆にしたグラフを返す
- void g.getDist(int root, int res[], void *mem=wmen) : ノードrootから各ノードまでの距離をBFSで求めます.辿り着けないノードまでの距離は -1 を返します.ワーキングメモリを使います.
- int g.getDist(int a, int b, void *mem=wmen) : ノードaとノードbの間の最短距離をBFSで求めます.辿り着けない場合は -1 を返します.ワーキングメモリを使います.
- int g.TreeDiameter(void *mem=wmen) : グラフの直径を返します.つまりdist(a,b)の最大値を返します.木であると仮定します.
- int g.TreeDiameter(int &a, int &b, void *mem=wmen) : グラフの直径とdist(a,b)が最大となるようなa,bを1組求めます.木であると仮定します.
- template<class S1, class S2> void getDistTree_WeightedNode_max(int root, S1 w[], S2 res[], void *mem = wmem) : 木であると仮定します.ノードrootから各ノード i までに通るノードの重み w[k] の最大値 res[i] を求めます.
- void g.SubTreeSize(int root, int res[], void *mem=wmen) : 根をrootとして,各ノードiを根とする部分木に含まれるノードの数 res[i] を求めます.グラフが無向木・有向木じゃない場合は何が起こるか不定です.
- template<class S> void g.SubTreeWeight(int root, S weight[], S res[], void *mem=wmen) : 根をrootとして,各ノードiを根とする部分木に含まれるノードの重みの和 res[i] を求めます.グラフが無向木・有向木じゃない場合は何が起こるか不定です.
- template<class S> void g.void cntShortest(int root, int dist[], S cnt[], void *mem = wmem) : ノードrootから各ノードまでの距離と最短距離を達成する方法の数をBFSで求めます.辿り着けないノードまでの距離は -1,方法の数は 0 を返します.ワーキングメモリを使います.
- graph g.reduce(int tn, int ind[], int self_e = 0, int dep_e = 0, void **mem = &wmem) : ノード i を新しくノード番号を ind[i] と付け替えたグラフを返します.複数のノードを1つにまとめることができます.ind[i]=-1とするとノードiは消えます.self_e=0なら結果のグラフでセルフループとなる枝は削除されます.dep_e=0なら結果のグラフで同じノード間をつなぐ枝が複数あるなら1つを残し削除されます.tnは返すグラフのノード数です.
- int g.scc(int res[], void *mem = wmem) : 強連結成分分解.強連結の数が戻り値として返し,res[i]はノードiが何番の強連結に属すか返す.
- int g.bcc(int res[], void *mem = wmem) : 二重連結成分分解.二重連結の数が戻り値として返し,res[i]はノードiが何番の二重連結に属すか返す.
- int g.articulation(int res[], void *mem=wmem) : 無向グラフの関節点を列挙します.戻り値は関節点の数で,rep[]に関節点のノード番号を入れます.
- int g.shortestPath(const int s, const int t, int res[], void *mem=wmem) : ノードsからノードtへの最短路の1つをbfsで求める.戻り値は距離.戻り値3,配列 res={s,0,1,t} のような感じで,配列は距離+1個の要素になる.たどり着けないなら-1を返す.
- int g.TopologicalSort(int res[], void *mem=wmem) : トポロジカルソートした結果をresに格納する.i < j ならノードres[j]からノードres[i]に枝はない.成功したら1を返し,グラフがDAGじゃない場合は0を返す.
- int g.Grundy(int res[], void *mem=wmem) : DAGに対して各ノードのGrundy数を求めます.正確には,iから出ている枝の終点のノードからなる集合をSとすると,$\verb|res|[i] = \min \{k \in \mathbb{Z}_{\geq 0} \mid {}^\forall j \in S, \verb|res|[j] \neq k\}$となります.戻り値はDAGでなければ0を返し(resは計算されません),DAGなら1を返します.
- int g.preorder(int res[], int root=0, void *mem=wmem) : ノード番号 root から dfs した結果の行きがけ順(ノードにたどり着いた順番)にノード番号を格納した配列 res を求める.たどり着けたノード数 = res の要素数を戻り値として返す.dfs で使う枝は格納順に使用していきます.
- int g.shortestCycle(int res[] = NULL, void *mem=wmem) : 有向グラフに対して長さ最小のサイクルを求める.戻り値はサイクルの長さ.サイクルが存在しなければ-1を返す.長さ3の場合の res の中身は {3,9,0,3} の用な感じで長さ+1個の要素を使う.時間計算量は $O(N(N+M))$.
- int g.shortestUndirectedCycle_length(void *mem=wmem) : 無向グラフに対して長さ最小のサイクル(同じ枝を2回通らない)の長さを求める.自己ループがあれば1,多重枝があれば2,サイクルが存在しなければ-1を返す.時間計算量は $O(N(N+M))$.
- int g.shortestUndirectedCycle_length(const int node, void *mem=wmem) : 無向グラフに対して,ノード node を通る,長さ最小のサイクル(同じ枝を2回通らない)の長さを求める.自己ループがあれば1,多重枝があれば2,サイクルが存在しなければ-1を返す.時間計算量は $O(N(N+M))$.
- int g.maxIndependenceSet(int res[] = NULL, void *mem = wmem) : 単純無向グラフに対して,最大独立集合の要素数を返します.res[]は最大独立集合に含まれるノード番号を入れます.枝刈り全探索です.$N=100$ぐらいまでなら何とかなる気がします.
- ll g.countIndependenceSet(void *mem = wmem) : 単純無向グラフに対して,独立集合の数を返します.全探索です.$N=40$ぐらいまでなら何とかなる気がします.
- int g.bipartite(int res[] = NULL, void *mem = wmem) : 無向グラフに対して二部グラフかどうかを判定します.二部グラフなら1を,そうでないなら0を返します.res[i] は 0 か 1 で2色で塗り分けたときのノードiの色を返します.
{
int N = 6, M = 7; // 0 → 1 ← 2
int A[7] = {0, 0, 1, 2, 3, 4, 5}; // | ^ |
int B[7] = {1, 3, 2, 5, 4, 1, 4}; // v | v
// 3 → 4 ← 5
int i;
graph g;
int dist[6];
g.setEdge(N, M, A, B); // node 0: degree 2: 1 3
rep(i,N){ // node 1: degree 3: 0 2 4
wtF("node {i}: degree {g.es[i]}: "); // node 2: degree 2: 1 5
wt(g.edge[i](g.es[i])); // node 3: degree 2: 0 4
} // node 4: degree 3: 3 1 5
// node 5: degree 2: 2 4
g.getDist(3, dist);
wt(dist(N)); // 1 2 3 0 1 2
g.setDirectEdge(N, M, A, B); // node 0: degree 2: 1 3
rep(i,N){ // node 1: degree 1: 2
wtF("node {i}: degree {g.es[i]}: "); // node 2: degree 1: 5
wt(g.edge[i](g.es[i])); // node 3: degree 1: 4
} // node 4: degree 1: 1
// node 5: degree 1: 4
g.getDist(3, dist);
wt(dist(N)); // -1 2 3 0 1 4
}
枝に重みのあるグラフ(構造体)
wgraph構造体を使えます.wgraph<T> g; などで定義します.
- int g.N : ノード数
- int *g.es : g.es[i] はノード i から出ている枝の本数
- int **g.edge : g.edge[i][j] はノード i から出ている j 番目の枝が向かう先のノード番号
- T **g.cost : g.cost[i][j] はノード i から出ている j 番目の枝の重み
- graph g : 枝の重みを無視したグラフ
- void g.setEdge(int N, int M, int A[], int B[], T C[], void **mem=&wmem) : ノード数 N,枝数 M の無向グラフをセット(g.N, g.es, g.edgeを書き換え)します.(A[i], B[i])間に枝があります.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- void g.setDirectEdge(int N, int M, int A[], int B[], T C[], void **mem=&wmem) : ノード数 N,枝数 M の有向グラフをセットします.A[i]からB[i]に向かう枝があります.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- void g.setEdgeRootedTree(int N, int M, int A[], int B[], T C[], int root=0, int reorder=0, int cnv[] = NULL, void **mem = &wmem) : ノード数 N,枝数 M の根付き木をセットします.A[i], B[i] の順番は問わず,親から子に向かう枝のみを設定します.reorder=1の場合は,節点の番号を根に近いから振り直します(番号が大きくなる方向にのみ枝があるようになります).cnv[i] = k は番号を付け替えた後のノード i はもともとのデータではノード k だったことを意味します.ワーキングメモリを使用し,wmemは使用した分勝手に進みます.
- template<class S> void g.getDist(int root, S res[], S unreachable = -1, void *mem=wmen) : ノードrootから各ノードまでの距離をダイクストラ法で求めます.辿り着けないノードまでの距離は unreachable を返します.各枝の距離は非負と仮定します.ワーキングメモリを使います.時間計算量は $O(N + M \log N)$ 程度です.
- template<class S> S g.getDistT(int a, int b, S unreachable = -1, void *mem=wmen) : ノードaからノードbまでの距離をダイクストラ法で求めます.辿り着けないノードまでの距離は unreachable を返します.各枝の距離は非負と仮定します.ワーキングメモリを使います.時間計算量は $O(N + M \log N)$ 程度です.参考:yukicoder No.1283 - Extra Fee
- template<class S> void g.getDistDense(int root, S res[], S unreachable = -1, void *mem=wmen) : ノードrootから各ノードまでの距離をヒープを使わないナイーブなダイクストラ法で求めます.辿り着けないノードまでの距離は unreachable を返します.各枝の距離は非負と仮定します.ワーキングメモリを使います.時間計算量は $O(N^2 + M)$ 程度です.
- template<class S> void g.getDistForest(int root, S res[], S unreachable = -1, void *mem=wmen) : 森の場合,ノードrootから各ノードまでの距離をBFSで求めます.辿り着けないノードまでの距離は unreachable を返します.ワーキングメモリを使います.
- template<class S> void g.BellmanFord(int root, S res[], S unreachable = -1, S minusInf = -2, int step = -1, void *mem = wmem) : ノードrootから各ノードまでの距離をBellman-Ford法で求めます.辿り着けないノードまでの距離は unreachable を,負閉路のためいくらでも距離が短くなる場合は minusInf を返します.stepが-1でなければ,rootからstep回以下移動する場合の最短距離を返します(minusInfにはならない).ワーキングメモリを使います.
- T g.MST_Prim_cost(void *mem = wmem) : 無向グラフだと仮定して,最小全域木の重み和を返す.Prim法を使う.
- int g.MST_Prim_cost(T &res, void *mem = wmem) : 無向グラフだと仮定して,最小全域木の重み和をresに代入して求める.戻り値は全域木が存在するなら1,存在しないなら0を返す.Prim法を使う.
Heavy-Light Decomposition(HLD,重軽分解)
根付き無向木に対してHeavy-Light Decomposition(HLD)を計算します.
根はノード0を利用します.
計算したHLDを用いて,LCAなどを高速に求めることができます.
また,segtreeを載せることができます.
HLD hld; で定義したとします.
- void hld.init(graph g, void **mem = &wmem) : グラフ g をセットします.以降で graph g を破壊しないでください.
- int hld.lca(int x, int y) : ノードxとノードyのLCAを求める.$O(\log N)$ 時間.
- int hld.depth(int x) : ノードxの深さ(ノード0からの距離)を求める.$O(\log N)$ 時間.
- int hld.dist(int x, int y) : ノードxとノードyの間の距離を求める.$O(\log N)$ 時間.
- int hld.up(int x, int d = 1) : ノードxのd世代親のノード番号を求める.存在しないなら-1.$O(\log N)$ 時間.
以下 HLD hld; の多分使わない情報.
- int hld.N : ノード数.graphと同じ.
- int *hld.es : 次数.graphと同じ.
- int **hld.edge : 枝.graphと同じ.
- int groupNum : グループ(Lightの枝で結ばれるパス)の数
- int *groupSize : i番目のグループに属するノード数が groupSize[i]
- int **groupNode : i番目のグループに属するj番目のノード番号が groupNode[i][j]
- int *groupUpNode : i番目のグループから根に向かってHeavyの枝を伝って登った先のノード番号が groupUpNode[i](一番上のグループなら-1)
- int *groupDepth : グループを1つのノードと見たときに,i番目のグループの根からの距離 groupDepth[i]
- int *group : ノードiが属するグループ番号が group[i]
- int *groupind : ノードiはグループ group[i] の groupind[i] 番目のノード
Fenwick treeを載せるときは,HLD_fenwick<T> t; を定義します.
ノードに値を持ちます.
(枝に値をもたせたい場合は,すぐ上に向かう枝の値をノードに持たせて,LCAでパスを分割したりするとできるかも)
- void t.init(HLD *hld__, void **mem = &wmem) : 初期設定.全節点は0で初期化されます.
- void t.add(int u, int v, T val) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値にvalを足します
- T t.get(int u, int v) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値の和を求めます
以下 HLD_fenwick<T> t; の多分使わない情報.
- HLD *t.hld : そうです.
- fenwick<T> *fen : fen[i]はグループiのfenwickです.
segtreeを載せるときは,HLD_segtree<T> t; を定義します.
ノードに値を持ちます.
- void t.init(HLD *hld__, T initval[], void **mem = &wmem) : 初期設定.initval[i] はノード i の初期値です.0で初期化するなら initval は NULL でも良い.
- void t.change(int u, int v, T val) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値をvalに置き換えます
- void t.add(int u, int v, T val) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値にvalを足します
- T t.getSum(int u, int v) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値の和を求めます
- pair<T,int> t.getMin(int u, int v) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値の(最小値,その最小値を達成するノード番号の1つ)を求めます
- T t.getMinVal(int u, int v) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値の最小値を求めます
- int t.getMinInd(int u, int v) : ノードuからノードvに向かうバス上の全てのノード(uもvも含む)の値の最小値を達成するノード番号の1つを求めます
枝に重みが載っていると思った場合の関数は以下の通り(実際は枝(x,y)の重みはx,yの内,深い方のノードに持たせていて,内部でLCAやら色々呼び出して辻褄合わせたりするので多分遅め)
- void t.change_edge(int u, int v, T val)
- void t.add_edge(int u, int v, T val)
- T t.getSum_edge(int u, int v)
- pair<T,int> t.getMin_edge(int u, int v)
- T t.getMinVal_edge(int u, int v)
- int t.getMinInd_edge(int u, int v)
以下 HLD_segtree<T> t; の多分使わない情報.
- HLD *t.hld : そうです.
- segtree<T> *seg : seg[i]はグループiのsegtreeです.
最大流(maxflow, Dinic)
最大流は maxflow<int,ll> flow; などで定義します.この場合,各辺の流量は int 型ですが,合計で流れる流量は ll 型です.
以下の説明では maxflow<T,S> flow; とします.
- int flow.node : ノード数
- T flow.eps : これ以下だと 0 だと思う閾値(S = double のときなどに使われる,デフォルトだと 1e-9.init() 関数,setGraph() 関数などでセットされる)
- int *flow.es : flow.es[i] でノード i から出ている枝の本数
- int **flow.edge : flow.edge[i][j] はノード i から出ている j 番目の枝が向かう先のノード番号
- int **flow.rev : ノード i から出ている j 番目の枝の逆向きの枝がノード j から出ている flow.edge[i][j] 番目の枝
- T **flow.flow : ノード i から出ている j 番目の枝にそって後どれぐらい流せるか
- flow.malloc(int N) : ノード数 N のグラフを確保できるメモリを確保(枝の分のメモリは確保しない)
- flow.walloc(int N, void**mem = &wmem) : ノード数 N のグラフを確保できるメモリを確保(枝の分のメモリは確保しない)
- flow.init(int N) : グラフを初期化,ノード数を N に設定
- flow.addEdge(int n1, int n2, T f1, T f2 = 0) : ノード n1 から n2 に向かって流量 f1,ノード n2 から n1 に向かって流量 f2 の枝を張る
- flow.setGraph(int N, int M, int n1[], int n2[], T f1[], T f2[]) : ノード数 N,枝数 M のグラフを設定
- void setGraph_w(int N, int M, int n1[], int n2[], T f1[], T f2[], void **mem = wmem) : ノード数 N,枝数 M のグラフを設定
- S solve(int st, int ed) : ノード st からノード ed までflowを流して,流れた量を返す
以下のコードは http://www.spoj.com/problems/FASTFLOW/ を解くコード.
int A[30000], B[30000], C[30000];
{
int N, M;
ll res;
maxflow<int,ll> f;
rd(N,M);
rd((A,B,C)(M));
A[0..M-1]--;
B[0..M-1]--;
f.malloc(N);
f.init(N);
f.addEdge(A[0..M-1],B[0..],C[0..],C[0..]);
res = f.solve(0, N-1);
wt(res);
}
最小費用流(minCostFlow)
最大流は minCostFlow<int,double> flow; などで定義します.この場合,流量は int 型で,コストは double 型です.
以下の説明では minCostFlow<FT,CT> flow; とします.
徐々に流していくアルゴリズムを適当に実装したもので,少なくても現状は良い実装ではないと思います.
負の閉路などがあると動きません.
- int flow.node : ノード数
- FT flow.eps : これ以下だと 0 だと思う閾値(FT = double のときなどに使われる,デフォルトだと 1e-9.init() 関数などでセットされる)
- int *flow.es : flow.es[i] でノード i から出ている枝の本数
- int **flow.edge : flow.edge[i][j] はノード i から出ている j 番目の枝が向かう先のノード番号
- int **flow.rev : ノード i から出ている j 番目の枝の逆向きの枝がノード j から出ている flow.edge[i][j] 番目の枝
- FT **flow.flow : ノード i から出ている j 番目の枝にそって後どれぐらい流せるか
- flow.malloc(int N) : ノード数 N のグラフを確保できるメモリを確保(枝の分のメモリは確保しない)
- flow.init(int N) : グラフを初期化,ノード数を N に設定
- flow.addEdge(int n1, int n2, FT f, CT c) : ノード n1 から n2 に向かって流量 f,コスト c の枝を張る
- template<class FTS, class CTS> void solve(int st, int ed, FTS &fres, CTS &cres, FT flim = -1) : ノード st からノード ed までflowを流して,流れた量をfresに,コストをcresに代入する.ただし,最大で流量flimまでしか流さない.flim=-1なら流せるだけ流す,flim=-2ならコストが負の間だけ流す.
参考:yukicoder No.1288 - yuki collection
Trie
Trie t; で定義したとします.
最終的なノード数の上限を n(例えば食わせる文字列の文字数の和+1),アルファベットのサイズ(文字の種類数)を k として,t.malloc(n, k); などでメモリの確保 + 初期化します.
n=10^6, k=26 で100メガバイト以上メモリが必要なので注意してください(nk*sizeof(int)は最低限必要で,それ以外も細々と必要かも).
ルート(空文字)に対応するノード番号は 0 です.
- int t.node : 現在のグラフのノード数
- void t.malloc(const int n, const int k) : ノード数の上限を n,アルファベットサイズの上限を k としてメモリ確保.
- void t.walloc(const int n, const int k, void **mem = &wmem) : mallocのワーキングメモリ版
- void t.free(void) : mallocで確保したメモリの解放
- void t.init(void) : 初期化(空文字を表すノード1個のみに戻す,アルファベットのサイズはメモリ確保時の上限に設定)
- void t.init(const int k) : 初期化(空文字を表すノード1個のみに戻す,アルファベットのサイズをkに設定)
- template<class T> int t.addWord(const T word[], const int len) : ルートから word[] に対応するパスを作り,到達地点の(wordに対応する)ノード番号を返す.
- template<class T> inline int t.addNext(const int n, const T c) : ノード n から1文字 c を付け加えたノード番号を返す.存在しないなら,そのノードを作って,そのノード番号を返す.
- template<class T> inline int t.next(const int n, const T c) : ノード n から1文字 c を付け加えたノード番号を返す.存在しないなら -1 を返す.
AhoCorasick
AhoCorasick aho; で定義したとします.
最終的なノード数の上限を n(例えば食わせる文字列の文字数の和+1),アルファベットのサイズ(文字の種類数)を k として,aho.malloc(n, k); でメモリの確保+初期化します.
アルファベットは0からk-1までの数値です.
文字列を addWord でいくつか加えてTrieぽいグラフを作る.
そして,construct で failedリンクを作って準備完了です.
- int aho.node : グラフのノード数
- void aho.init(void) : 初期化(空文字を表すノード1個のみに戻す)
- template<class T> int aho.addWord(const T word[], const int len, const int id) : Wordを追加する.Trieにおける終点のノード番号を返す
- void aho.construct(void *mem = wmem)
- template<class T> inline void next(const int n, const T c) : ノードnにいるときに次にcの文字で遷移した先のノードを返す.constructを呼び出した後に使える.
- int aho.indsz[n] : ノードnが終端となるような文字列の数
- int aho.ind[n][k] : ノードnが終端となるようなk個目の文字列のid(idはaddWordで食わせたもの)
全ての文字列を区別する必要がなければ以下のもので十分かもしれません.
AhoCorasick_Sum<S> aho; で定義したとします.
最終的なノード数の上限を n(例えば食わせる文字列の文字数の和+1),アルファベットのサイズ(文字の種類数)を k として,aho.malloc(n, k); でメモリの確保+初期化します.
アルファベットは0からk-1までの数値です.
文字列を addWord でいくつか加えてTrieぽいグラフを作る.
そして,construct で failedリンクを作って準備完了です.
- int aho.node : グラフのノード数
- void aho.init(void) : 初期化(空文字を表すノード1個のみに戻す)
- template<class T> int aho.addWord(const T word[], const int len, const S val)
- void aho.construct(void *mem = wmem)
- template<class T> inline void next(const int n, const T c) : ノードnにいるときに次にcの文字で遷移した先のノードを返す.constructを呼び出した後に使える.
- S aho.sum[n] : ノードnが終端となるような文字列のvalの和
AhoCorasickの使い方の参考:
AtCoder Beginner Contest #122 D問題 - We Like AGC
Polynomial(1変数多項式)
あまりちゃんと作っていません.
Polynomial<T> P; で作ります.定義域,値域ともにTです.
疎構造ではないので,次数の高い多項式は問答無用でたくさんの項を扱うことになって遅いです.
今のところ愚直な実装です.
コンストラクタ
- Polynomial() : 恒等的に0の多項式
- Polynomial(const Polynomial<T> &a)
デストラクタ
その他関数 [メンバ関数]
- inline void P.change(const int dg, const T cf) : 次数dgの係数をcfに変更する
- inline int P.deg(void) : 次数を返す.定数関数(常に0も含む)の次数は0
- inline T P.coef(const int k) : $x^k$ の係数を返す.k が次数より大きければちゃんと 0 を返す.
オペレーター [メンバ関数]
- Polynomial<T>& operator=(const Polynomial<T> &a)
- Polynomial<T>& operator+=(const Polynomial<T> &a)
- Polynomial<T> operator+(const Polynomial<T> &a)
- Polynomial<T>& operator-=(const Polynomial<T> &a)
- Polynomial<T> operator-(const Polynomial<T> &a)
- Polynomial<T>& operator*=(const Polynomial<T> &a)
- Polynomial<T> operator*(const Polynomial<T> &a)
- Polynomial<T>& operator/=(const Polynomial<T> &a)
- Polynomial<T> operator/(const Polynomial<T> &a)
- Polynomial<T>& operator%=(const Polynomial<T> &a)
- Polynomial<T> operator%(const Polynomial<T> &a)
- Polynomial<T>& operator*=(const T &a)
- Polynomial<T> operator*(const T &a)
- Polynomial<T>& operator/=(const T &a)
- Polynomial<T> operator/(const T &a)
- inline T operator()(const T x)
オペレーター [メンバ関数以外]
- template<class T> Polynomial<T> operator*(const T a, const Polynomial<T> &b)
使わなさそうなの
- int d : 次数を格納する変数
- T *c : 係数を格納する配列(c[i]はx^iの係数)
- int mem : 係数を保存する配列 c でメモリを格納しているサイズ
- void expand(int z) : 係数を保存する配列 c のサイズを z 個以上格納できるように拡張する
double x;
Polynomial<double> P, Q, R1, R2; // P = Q = 0
P.change(2, 1); // P = x^2
P.change(1, 2); // P = x^2 + 2*x
P.change(0, 1); // P = x^2 + 2*x + 1
x = P(2); // P(2) = 2^2 + 2*2 + 1 = 9
Q.change(1, 1); // Q = x
Q.change(0, 4); // Q = x + 4
// x^2 + 2*x + 1 = (x-2) (x+4) + 9
R1 = P / Q; // x - 2
R2 = P % Q; // 9
Q *= 2.0; // 2x + 8
// x^2 + 2x + 1 = (0.5x - 1) (2x + 8) + 9
R1 = P / Q; // 0.5x - 1
R2 = P % Q; // 9
Q.change(1, 0); // Q = 8
R1 = P / Q; // (1/8) x^2 + (1/4) x + (1/8);
R2 = P % Q; // 0
その他色々な関数など
- void walloc1d(T **arr, int x, void **mem = &wmem): 配列arrに要素数xだけワーキングメモリから割り当てる(ワーキングメモリは割当てた分は勝手に進む)
- void walloc1d(T **arr, int x1, int x2, void **mem = &wmem): arr[x1], arr[x1+1], ..., arr[x2-1] が使用できるように割り当てる
- void walloc2d(T ***arr, int x, int y, void **mem = &wmem): 二次元配列arrに要素数x*yだけワーキングメモリから割り当てる(ワーキングメモリは割当てた分は勝手に進む)
- void walloc2d(T ***arr, int x1, int x2, int y1, int y2, void **mem = &wmem): arr[x1][y1]~arr[x2-1][y2-1] が使えるように割り当てる
- void malloc1d(T **arr, int x): 配列arrに要素数xだけmallocを使ってメモリを割り当てる
- void malloc2d(T ***arr, int x, int y): 二次元配列arrに要素数x*yだけmallocを使ってメモリを割り当てる
- void free1d(T *arr): malloc1d() で確保したメモリを開放
- void free2d(T **arr): malloc2d() で確保したメモリを開放
int i, j;
int *a, **m, **hoge;
walloc1d(&a, 100);
walloc2d(&m, 50, 100);
rep(i,100) a[i] = i;
rep(i,50) rep(j,100) m[i][j] = i+j;
// ↓出力 *例* (sizeof(*a)=4の環境)
printf("%p %d\n",a,a); // 0x404040 4210752
printf("%p %d\n",m,m); // 0x4041d0 4211152
printf("%p %d\n",m[0],m[0]); // 0x404298 4211352
printf("%p %d\n",m[1],m[1]); // 0x404428 4211752
malloc2d(&hoge, 50, 100);
free2d(hoge);
int *a;
void *mem;
mem = wmem; // ワーキングメモリの先頭アドレスを覚えておく
walloc1d(&a, 1d6); // 10^6要素の配列を確保
/* 何かする */
wmem = mem; // ワーキングメモリの先頭アドレスを戻すことによって,配列aを解放
Rand構造体.乱数生成に使う.xorshift.
Rand rnd; で宣言したとして以下の通り.
何回か実行したとき,うまく種を設定しないと最初の数十回ぐらいは似た系列が出てくるので,気になる場合は,最初にgetを100回呼び出すなど空打ちすると良い.
一部仕様は未決定.
種を設定しない場合は,time(NULL)を使って設定.
- int rnd.get(int a): 0以上a未満の整数を一様にランダムに返す
- int rnd.get(int a, int b): a以上b以下の整数を一様にランダムに返す
- ll rnd.get(ll a): 0以上a未満の整数を一様にランダムに返す
- ll rnd.get(ll a, ll b): a以上b以下の整数を一様にランダムに返す
- double rnd.get(double a, double b): a以上b未満の実数を一様にランダムに返す
以下は微妙に仕様を決めきれてない.
- ll rnd.get(void): 0以上2^32未満の整数を一様にランダムに返す
- double rnd.getUni(void): 0以上1未満の実数を一様にランダムに返す
- ll rnd.getExp(int a): 0以上a未満の整数をランダムに返す.ただし,小さい値がでやすい.exp(k)の形に書いたとき,kの値が一様に分布な感じ.
- ll rnd.getExp(int a, int b): a以上b以下の整数をランダムに返す.ただし,小さい値がでやすい.exp(k)+aの形に書いたとき,kの値が一様に分布な感じ.
以下はそのうち実装予定でまだ作ってない
- void rnd.set(unsigned w): 種をセット
- void rnd.set(unsigned x, unsigned y, unsigned z, unsigned w): 種をセット
unionfind構造体.unionFind uf; で宣言したとして以下の通り.
- void uf.malloc(const int n): メモリ確保
- void uf.malloc(const int n, const int fg): メモリ確保.fgが0でなければnノードで初期化する
- void uf.free(void): mallocで確保したメモリの解放
- void uf.walloc(const int n, void **mem=&wmem): メモリ確保
- void uf.walloc(const int n, const int n, void **mem=&wmem): メモリ確保.fgが0でなければnノードで初期化する
- void uf.init(const int n): 初期化
- void uf.init(void): 確保したメモリの要素数で初期化
- int uf.get(int a): ノードaの一番親のノードを返す(uf.get(a)==uf.get(b)ならa,bは同じグループに属す)
- int uf.connect(int a, int b): aとbを同じグループにまとめる.aとbが既に同じグループなら0を返し,そうでなければ1を返す
- int uf.operator()(int a): get(a)と同じ
- int uf.operator()(int a, int b): connect(a,b)と同じ
- int uf.size(int a): ノードaが属すグループのノード数を返す
以下あまり使わないやつ
- int& uf.operator[](const int a): 内部の配列の値
- int uf.sizeList(int res[]): 各グループのサイズからなる配列を返す.返り値はグループ数.
rollbackUnionfind構造体.rollbackUnionFind uf; で宣言したとして以下の通り.
ただし,unionFindで使えるものは全てそのまま使える.
経路圧縮をしないので,一部の計算量はアッカーマン逆関数からlogになっている.
- void uf.snapshot() : スナップショットを撮る(1回もスナップショットを撮ってなければスナップショットは初期値の状況)
- void uf.rollback() : 最後にスナップショットを撮ったところまで巻き戻す
- int uf.undo() : connectなどで最後に非連結で繋げた枝を消す(connectの戻り値が0のものはカウントしない).スナップショットを撮ったより過去に戻った場合はスナップショットも巻き戻る
Timer構造体.時間を測るために使う.Timer timer;で宣言したとして以下の通り.gettimeofdayを使用する.
- void timer.set(): 初期化.
- double timer.get(): 最後にset()を呼び出してからの経過秒数を返す.
int i;
ll sum = 0;
Timer timer;
double tm;
timer.set();
rep(i,1,1d8) sum += 1d9 /+ i;
tm = timer.get();
wtF("time: {tm}, sum: {sum}\n"); // time: 0.678610086441040, sum: 19048729277
- void sortA(int N, S A[], T B[], ..., void *mem = wmem) : 要素数Nの1個~4個の配列を (A[i], B[i], ...) が辞書式順序で小さい順にソートします.ワーキングメモリを使用します.
- void rsortA(int N, S A[], T B[], ..., void *mem = wmem) : 要素数Nの1個~4個の配列を (A[i], B[i], ...) が辞書式順序で大きい順にソートします.ワーキングメモリを使用します.
- void sortV(vector<S> &A, vector<T> &B, ..., void *mem = wmem) : 同じ要素数の1個~4個のvectorを (A[i], B[i], ...) が辞書式順序で小さい順にソートします.ワーキングメモリを使用します.
- void rsortV(vector<S> &A, vector<T> &B, ..., void *mem = wmem) : 同じ要素数の1個~4個のvectorを (A[i], B[i], ...) が辞書式順序で大きい順にソートします.ワーキングメモリを使用します.
- void sortF(int N, unsigned A[], void *mem = wmem) : 基数ソートする.ただしNが256未満ならばstd::sortでソートする.こっちの方が速い気がするけど,環境・入力依存.配列の要素が大幅偏っているとstd::sortに負けるかも.
- void sortF(int N, int A[], void *mem = wmem) : 基数ソートする.ただしNが256未満ならばstd::sortでソートする.こっちの方が速い気がするけど,環境・入力依存.配列の要素が大幅偏っているとstd::sortに負けるかも.大体大丈夫だと思うけどもしかしたら正常に動かない環境があるかも.
- void sortF(int N, ull A[], void *mem = wmem) : 基数ソートする.ただしNが512未満ならばstd::sortでソートする.こっちの方が速い気がするけど,環境・入力依存.配列の要素が大幅偏っているとstd::sortに負けるかも.
- void sortF(int N, ll A[], void *mem = wmem) : 基数ソートする.ただしNが512未満ならばstd::sortでソートする.こっちの方が速い気がするけど,環境・入力依存.配列の要素が大幅偏っているとstd::sortに負けるかも.大体大丈夫だと思うけどもしかしたら正常に動かない環境があるかも.
- sortE(a, b, c, ...) : バブルソートで昇順にするコードに展開します.現状 a, b, c, ... は全て同じ型でないと動きません.
- rsortE(a, b, c, ...) : バブルソートで降順にするコードに展開します.現状 a, b, c, ... は全て同じ型でないと動きません.
- template<class T1, class T2> inline T1 Kth0(const T1 a, const T2 b) : a, b の中で小さい方(0-originで小さい方から0番目)を返す.
- template<class T1, class T2> inline T1 Kth1(const T1 a, const T2 b) : a, b の中で大きい方(0-originで小さい方から1番目)を返す.
- template<class T1, class T2, class T3> inline T1 Kth0(const T1 a, const T2 b, const T3 c) : a, b, c の中で最も小さいもの(0-originで小さい方から0番目)を返す.
- template<class T1, class T2, class T3> inline T1 Kth1(const T1 a, const T2 b, const T3 c) : a, b, c の中で中央値(0-originで小さい方から1番目)を返す.clamp.
- template<class T1, class T2, class T3> inline T1 Kth2(const T1 a, const T2 b, const T3 c) : a, b, c の中で最も大きいもの(0-originで小さい方から2番目)を返す.
- template<class T1, class T2, class T3, class T4> inline T1 Kth0(const T1 a, const T2 b, const T3 c, const T4 d) : a, b, c, d の中で0-originで小さい方から0番目を返す.
- template<class T1, class T2, class T3, class T4> inline T1 Kth1(const T1 a, const T2 b, const T3 c, const T4 d) : a, b, c, d の中で0-originで小さい方から1番目を返す.
- template<class T1, class T2, class T3, class T4> inline T1 Kth2(const T1 a, const T2 b, const T3 c, const T4 d) : a, b, c, d の中で0-originで小さい方から2番目を返す.
- template<class T1, class T2, class T3, class T4> inline T1 Kth3(const T1 a, const T2 b, const T3 c, const T4 d) : a, b, c, d の中で0-originで小さい方から3番目を返す.
- template<class T> inline T KthA(const int K, const int N, const T A[], void *mem = wmem) : 長さ N の配列 A[] の中で 0-origin で小さい方から K 番目の値を返す.配列A[]を破壊しない.内部でnth_elementを使用.
int i; // (1,2) ←出力結果
int A[5] = {3,1,3,5,2}; // (2,5)
int B[5] = {1,2,3,4,5}; // (3,1)
sortA(5,A,B); // (3,3)
rep(i,5) wtF("({A[i]},{B[i]})\n"); // (5,4)
- template<class T> void arrRot(int k, int N, T A[], T B[] = NULL, void *mem = wmem) : 長さNの配列Aに対して,B[i] = A[(i+k)%N] なる配列Bを計算する.B=NULLのときはAに上書きする.(左にkだけシフトします.先頭の要素を最後にくっつける操作をk回やる感じ.)kはN以上だったりマイナスでも大丈夫なはずです.
- template<class S> void arrErase(int k, int &sz, S a[]) : 要素数szの配列aのk番目の要素を消す.szは勝手に1減る.a[0]=1, a[1]=3, a[2]=4, sz=3 のとき arrErase(1,sz,a) によって a[0]=1, a[1]=4, sz=2 に変更される.
- template<class S, class T> void arrErase(int k, int &sz, S a[], T b[]) : 要素数szの配列aと配列bのk番目の要素を消す.
- template<class S, class T, class U> void arrErase(int k, int &sz, S a[], T b[], U c[]) : 要素数szの配列aと配列bと配列cのk番目の要素を消す.
- template<class S> void arrInsert(const int k, int &sz, S a[], const S aval) : 要素数szの配列aに要素を a[k] = aval になるように挿入する.
- template<class S, class T> void arrInsert(const int k, int &sz, S a[], const S aval, T b[], const T bval)
- template<class S, class T, class U> void arrInsert(const int k, int &sz, S a[], const S aval, T b[], const T bval, U c[], const U cval)
- template<class S, class T, class U, class V> void arrInsert(const int k, int &sz, S a[], const S aval, T b[], const T bval, U c[], const U cval, V d[], const V dval)
- template<class S, class T> inline int arrcmp(int As, S A[], int Bs, T B[]) : 長さAsの配列Aと長さBsの配列Bを辞書順で大小比較します.A > B なら 1,A = B なら 0,A < B なら -1 を返します(strcmp的な感じ).
- template<class S> int arrEraseVal(S val1, int &sz, S a[]) : 要素数szの配列aの中で値がval1の要素を全て消す.szは勝手に消された分減る.消した要素の数を戻り値として返す.
- template<class S> int arrEraseVal(S val1, S val2, int &sz, S a[]) : 要素数szの配列aの中で値がval1またはval2の要素を全て消す.szは勝手に消された分減る.消した要素の数を戻り値として返す.
- template<class S> int arrEraseVal(S val1, S val2, S val3, int &sz, S a[]) : 要素数szの配列aの中で値がval1またはval2またはval3の要素を全て消す.szは勝手に消された分減る.消した要素の数を戻り値として返す.
- template<class S> int arrEraseVal(S val1, S val2, S val3, S val4, int &sz, S a[]) : 要素数szの配列aの中で値がval1またはval2またはval3またはval4の要素を全て消す.szは勝手に消された分減る.消した要素の数を戻り値として返す.
- template<class S> int vecEraseVal(S val1, vector<S> &a) : 配列aの中で値がval1の要素を全て消す.消した要素の数を戻り値として返す.
- template<class T> int Distinct(int N, T A[], int sorted=0, void *mem = wmem) : 要素数 N の配列 A[] 中の,相異なる要素の数を返します.O(N log N) 時間,ソート済みなら O(N) 時間.
- template<class T1, class T2> inline int DistinctE(T1 a, T2 b) : a, b の中で異なる要素数を返します.
- template<class T1, class T2, class T3> inline int DistinctE(T1 a, T2 b, T3 c) : a, b, c の中で異なる要素数を返します.
- template<class T1, class T2, class T3, class T4> inline int DistinctE(T1 a, T2 b, T3 c, T4 d) : a, b, c, d の中で異なる要素数を返します.
- template<class T, class S> int arrCountVal(int N, T A[], S val) : 要素数 N の配列 A[] 中の val と一致する要素数を返します.
- template<class T> int Mex(int N, T A[], int sorted=0, void *mem = wmem) : 要素数 N の配列 A[] の mex を返します.つまり,非負整数で配列 A[] に存在しない最小の値を返します.
- template<class T1> inline int Mex(T1 a) : Mex(1, {a})
- template<class T1, class T2> inline int Mex(T1 a, T2 b) : Mex(2, {a, b})
- template<class T1, class T2, class T3> inline int Mex(T1 a, T2 b, T3 c) : Mex(3, {a, b, c})
- template<class S, class T> int HammingDistance(int N, S A[], T B[]) : 配列 A, B のハミング距離を求めます.
- template<class T> int editDistance(int As, T A[], int Bs, T B[], void *mem = wmem) : 配列 A, B の編集距離を求めます.時間計算量 O(As*Bs) ぐらいのDP.
- template<class T> ll counterSumIsLT(int As, T A[], int Bs, T B[], T val) : 配列 A, B はソートされているとします.A[i] + B[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As+Bs) ぐらいの尺取法.
- template<class T> ll counterProdIsLT(int As, T A[], int Bs, T B[], T val) : 配列 A, B はソートされているとします.A[i] * B[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As+Bs) ぐらいの尺取法.
- template<class T> ll counterD2SumIsLT(int As, T A[], T val) : 配列 A はソートされているとします.i < j であって,A[i] + A[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As) ぐらいの尺取法.
- template<class T> ll counterM2SumIsLT(int As, T A[], T val) : 配列 A はソートされているとします.i ≤ j であって,A[i] + A[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As) ぐらいの尺取法.
- template<class T> ll counterD2ProdIsLT(int As, T A[], T val) : 配列 A はソートされているとします.i < j であって,A[i] * A[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As) ぐらいの尺取法.
- template<class T> ll counterM2ProdIsLT(int As, T A[], T val) : 配列 A はソートされているとします.i ≤ j であって,A[i] * A[j] が val 未満になる (i,j) の数を返します.時間計算量 O(As) ぐらいの尺取法.
x if[c1,s1,c2,s2,e] y; は if(c1){ x s1 y; } else if(c2){ x s2 y; } else { x e y; } に展開される.(c1,s1)のペアはいくつ合っても良い.eは空なら省略可能.
ただし,現状if文などの()の中では使用不可能.
例えば
a[i] = x if[i%2==0,+,-] y;
s[i] = b[i] if[i,+s[i-1]];
は以下のように展開されます:
if(i%2==0){
a[i] = x + y;
} else {
a[i] = x - y;
}
if(i){
s[i] = b[i] + s[i-1];
} else {
s[i] = b[i];
}
式に対して最小値,最大値,和などを求める.
最小値の場合は,
min[型][範囲](式)
の形式で [型] は省略可能(int型をたくさん足してll型で結果を得たい場合などは [型] を指定する).
範囲を表す変数は int 型ですが,他の型の場合は範囲の書き方を見てください.
範囲が空なら,sumは0,mulは1,gcdは0,lcmは1になります.minは型が表せる最大の値,maxは型が表せる最小の値になります.
- min[型][範囲](式): 最小値を求める
- max[型][範囲](式): 最大値を求める
- sum[型][範囲](式): 和を求める
- mul[型][範囲](式): 積を求める
- gcd[型][範囲](式): gcdを求める
- lcm[型][範囲](式): lcmを求める
- argmin[型][範囲](式): 式を最小にするような変数のうち最初のものを求める(範囲は1変数のみ対応).
- argmax[型][範囲](式): 式を最大にするような変数のうち最後のものを求める(範囲は1変数のみ対応).
- argminL[型][範囲](式): 式を最小にするような変数のうち最初のものを求める(範囲は1変数のみ対応).
- argmaxL[型][範囲](式): 式を最大にするような変数のうち最後のものを求める(範囲は1変数のみ対応).
範囲の書き方は以下のとおりです.
各変数の動く範囲を,区切りで記述します(変数は定義されていなくても構いません,式の評価にのみ利用します).
i=a---b : 変数 i を a から b まで(両端を含む)動かします.
i,a,b : 変数 i を a から b-1 まで(両端を含む)動かします.
(i,j)=a---b : 変数 i と j を a から b まで(両端含む)動かします.
(i,j),a,b : 変数 i と j を a から b まで(両端含む)動かします.
変数の型を指定する場合は,変数名の前につけてください.
例えば (i,j),a,b において i は int 型だけど,j を ll 型にする場合は (i, ll j),a,b としてください.
また,配列に対する argmin, argmax は以下の形式も使えます.
- argmin(arr(n)): argmin[var=0---n-1](arr[var]) 長さnの配列arrの最小値の添え字を返す.複数存在するなら最小の添え字を返す.
- argmax(arr(n)): argmax[var=0---n-1](arr[var]) 長さnの配列arrの最大値の添え字を返す.複数存在するなら最大の添え字を返す.
- argminL(arr(n)): argminL[var=0---n-1](arr[var]) 長さnの配列arrの最小値の添え字を返す.複数存在するなら最小の添え字を返す.
- argmaxL(arr(n)): argmaxL[var=0---n-1](arr[var]) 長さnの配列arrの最大値の添え字を返す.複数存在するなら最大の添え字を返す.
{
int i;
i = min[k=1---100](abs(4*k-50));
wt(i); // 2 と表示
i = argmin[k=1---100](abs(4*k-50));
wt(i); // 12 と表示
}
{
int A[3][3] = {
300000000, 500000000, 700000000,
300000000, 200000000, 700000000,
300000000, 500000000, 900000000
};
wt(min[i,0,3,j,0,3](A[i][j]) + max[i,0,3,j,0,3](A[i][j])); // 1100000000 = 200000000 + 900000000
wt(sum[ll][i,0,3,j=0---2](A[i][j])); // 4400000000 (ll型なのでオーバーフローしない)
wt(gcd[(i,j),0,3](A[i][j])); // 100000000
wt(lcm[ll][(i,j),0,3](A[i][j])); // 63000000000
}
{
wt(max[ll i,1d10,1d10+20](i+2)); // 10000000021
}
- bsearch_min[type,name,minval,maxval(,option)][block](cond): 「blockを実行した上でcondを満たす」最小の minval ≤ name ≤ maxval の値を返す.(,option),[block]は省略可能.
- bsearch_max[type,name,minval,maxval(,option)][block](cond): 「blockを実行した上でcondを満たす」最大の minval ≤ name ≤ maxval の値を返す.(,option),[block]は省略可能.
それぞれ単調性が成り立たなければならない.
例えば,bsearch_minの場合,name=10のときcondが成り立つならば,name=10以上(maxval以下)のときもcondは成り立たなければならない.
typeは変数nameの型を指定し,float, doubleの場合は実数の二分探索,それ以外は整数の二分探索を行う.
bsearch_minでは,name=maxvalでは評価されず,name=maxvalより小さい場合に常に偽ならmaxvalが返ってくる(name=maxvalのときはblock, condが未定義になるようなものでも良い).
今のところ,minval, maxvalは省略不可能.
optionは以下の通り.
- AE=val : 実数の二分探索で,絶対誤差でval以下が保証できるようになったら打ち切る.
- RE=val : 実数の二分探索で,相対誤差でval以下が保証できるようになったら打ち切る.
- E=val : 実数の二分探索で,絶対誤差または相対誤差でval以下が保証できるようになったら打ち切る.
実数の二分探索で,許容誤差を指定しない場合は,見ている範囲を[x,y]としたとき,(x+y)/2が(計算機上で)xまたはyに一致したら打ち切る(許容誤差を指定してもこの判定は行う).
int i, j, k, t;
double x, y;
k = -1;
i = bsearch_max[int,k,0,100](k*k<=10);
j = bsearch_min[int,k,0,100][
t = 0;
rep(j,1,k+1) t += k / j;
](t >= 100);
x = bsearch_max[double,x,0,100](x*x<=10);
y = bsearch_max[double,x,0,100,E=1e-1](x*x<=10);
wt(i,j,k,x,y); // 3 28 -1 3.162277660168380 3.222656250000000
三分探索(内部実装は少し違うかも)のようなもの.二分探索とほぼ同様の書き方.
- tsearch_min[type,name,minval,maxval(,option)][block](eq): name=kを代入してblockを実行した上でeqを計算するとき,最小のeqの値を返す.ただし,minval ≤ name ≤ maxval の間を動く.
- tsearch_max[type,name,minval,maxval(,option)][block](eq): 同様に最大の値を返す
- tsearch_argmin[type,name,minval,maxval(,option)][block](eq): 最小となるときのnameの値を返す.複数あるならその中で最小のものを返す.
- tsearch_argminL[type,name,minval,maxval(,option)][block](eq): 最小となるときのnameの値を返す.複数あるならその中で最大のものを返す.
- tsearch_argmax[type,name,minval,maxval(,option)][block](eq): 最大となるときのnameの値を返す.複数あるならその中で最小のものを返す.
- tsearch_argmaxL[type,name,minval,maxval(,option)][block](eq): 最大となるときのnameの値を返す.複数あるならその中で最大のものを返す.
optionは以下の通り.
- T=TYPE : eqの型がTYPEであることを指定する.blockの中で変数を定義してeqの中で使う場合,型を推測できないので必要になる.
最小値を求める場合,name=kのときのeqの値を f(k) と書くことにしたら,とある L ≤ R が存在して
minval ≤ x < y ≤ L のとき f(x) > f(y),
L ≤ x < y ≤ R のとき f(x) = f(y),
R ≤ x < y ≤ maxval のとき f(x) < f(y)
が成り立たなければならない.
{
wt(tsearch_min[ll,x,-1d9,1d9](x*(x+10))); // -25
wt(tsearch_argmin[ll,x,-1d9,1d9](x*(x+10))); // -5
wt(tsearch_max[ll,x,-1d9,1d9,T=ll][ll y; y = x + 5;](-(y-5)*(y+5))); // 25
wt(tsearch_argmax[ll,x,-1d9,1d9,T=ll][ll y; y = x + 5;](-(y-5)*(y+5))); // -5
}
参考:Codeforces Round #679 DIV1 C問題 - Solo mid Oracle
- template<class T> inline int isPrime(T N): N が素数なら 1,そうでないなら 0 を返す.$O(\sqrt{N})$ 時間.
- int Prime(int N, int res[], void *mem = wmem): N 未満の素数を列挙する.
- template<class T> void intervalSieve(ll st, int len, T res[], int ps, int p[]) : 区間篩.res[i]はst+iが素数なら1,そうでないなら0が代入される(i=0,1,...,len-1).ps, p[]としてsqrt(st+len-1)以下の素数を列挙しておく必要がある.
- template<class T> int Factor(T N, T fac[], int fs[]): 素因数と重複度を求める.下記の例を参照.facは異なる素因数,fsはその素因数を何個持つか.Tはint, llなどの整数.
- template<class T> int Factor(T N, T fac[]): 相異なる素因数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T> int Factor(T N): 相異なる素因数の個数を求める.Tはint, llなどの整数.
- template<class T> int FactorM(T N, T fac[]): 重複を含めて素因数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T> int Divisor(T N, T dv[], void *mem = wmem): 約数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T> ll DivisorSum(ll N, void *mem = wmem): 約数の和を求める.
- template<class T> int Moebius_L(T n): メビウス関数の値を求める.nが平方数で割り切れるなら0を返し,そうでないなら素因数の数を $k$ として $(-1)^k$ を返す.
int fac[100], fs[100], fsz;
int dv[100], dsz;
fsz = Factor(1500, fac, fs); // 1500 = 2^2 * 3^1 * 5^3
rep(i,fsz) wt(fac[i], fs[i]); // fsz=3, fac={2,3,5}, fs={2,1,3}
fsz = Factor(1500, fac);
wt(fac(fsz)); // 2 3 5
fsz = FactorM(1500, fac);
wt(fac(fsz)); // 2 2 3 5 5 5
dsz = Divisor(24, dv);
wt(dv(dsz)); // 1 2 3 4 6 8 12 24
- ull powmod(ull a, ull b, ull m) : a の b 乗を mod m で求める.a は 32bit 以内の整数.
- ll primitiveRoot(ll p, void *mem = wmem) : 素数 p に対して,mod p での最小の原子根を返す.p は 32bit 以内の整数.pが素数でなければ -1 を返すことにしているが将来的には素数でない場合の挙動は変わるかも.時間計算量は $O(\sqrt{p} + yk \log p)$ 程度,ただし y は p-1 の約数の個数,k は答え=最小の原子根.
- int get_inv_mod(ll a, int md) : mod md での a の逆元を返す.gcd(a, md) = 1 が必要.
- void extendedEuclid(ll a, ll b, ll &x, ll &y, ll &z) : ax + by = z を満たす x, y, z を求める.ただし,z = gcd(|a|,|b|) ≥ 0 で x, y は xは非負で最小となるようなものを返す.a = b = 0 の場合は x = y = z = 0 を返す.
- void extendedEuclid(ll a, ll b, ll &x, ll &y) : 上の関数でzが不要な場合.
- ll chineseRemainder(int sz, ll val[], ll md[]) : 中国剰余定理CRT.x = val[k] (mod md[k]), k = 0, 1, ..., sz-1 が成り立つような最小の非負整数 x を返す.存在しないなら -1 を返す.lcm(md[]) が 2^62 程度まで.val[k] は 0 以上 md[k] 未満である必要はない.
- gcd(), GCD(): 引数の最大公約数を返します.rd, wtのように配列を指定可能です.
- lcm(), LCM(): 引数の最小公倍数を返します.rd, wtのように配列を指定可能です.
- min(), MIN(): 引数の最小値を返します.rd, wtのように配列を指定可能です.
- max(), MAX(): 引数の最大値を返します.rd, wtのように配列を指定可能です.
- sum(), SUM(): 引数の和を返します.rd, wtのように配列を指定可能です.
- mul(), MUL(): 引数の積を返します.rd, wtのように配列を指定可能です.
関数名がよく使う名前なのでこの機能を無効化するフラグがあります(フラグの欄を参照のこと).
引数の型は違っても構いませんが,計算途中でどのような型になるかは不明です(オーバーフローなどに注意).
今のところ,整数と浮動小数点数ぐらいでしか上手く動かないと思います.例外的にsum()はstring型でも動くはずです(文字列を連結します).
配列は長さ0の配列を与えると上手く動かないことがあります.
int i = 10, j = 75;
int k[3] = {100, 200, 300};
wt(gcd(i,j,k(3))); // gcd(10,75,100,200,300) = 5
wt(min(i,j,k(3))); // min(10,75,100,200,300) = 10
wt(sum(i,j,k(3))); // sum(10,75,100,200,300) = 685
- template<class T> void slideMin(int n, int k, T in[], T res[], void *mem = wmem) : スライド最小値.長さnの配列inに対して,長さkの区間の最小値を全て求めます.res[i] = min(in[i], ..., in[i+k-1]) になります(i=0,1,...,n-k).時間計算量 $O(n)$.
- template<class T> void slideMax(int n, int k, T in[], T res[], void *mem = wmem) : スライド最大値.長さnの配列inに対して,長さkの区間の最大値を全て求めます.res[i] = max(in[i], ..., in[i+k-1]) になります(i=0,1,...,n-k).時間計算量 $O(n)$.
Comb 構造体は(1つ下のconbination_mintのように)階乗などを前計算しておき組合せ的な数を求める構造体です.
Comb<T> comb; で定義したとします.T は mint, modint, Mint, Modint のどれかを想定しています.
必要な値がない場合は,必要になったときに勝手に計算します.
使いそうなの
- T comb.C(int a, int b) : 二項係数 C(a,b) = $\frac{a!}{b!(a-b)!}$ を返します(bが負,a以上のときは0を返します).
- T comb.P(int a, int b) : P(a,b) = $\frac{a!}{(a-b)!}$ を返します
- T comb.H(int a, int b) : 重複組合せ H(a,b) = C(a+b-1, b) を返します
- T comb.fac(int k) : 階乗 $k!$ を返します
- T comb.ifac(int k) : 階乗の逆数 $\frac{1}{k!}$ を返します
- T comb.Multinomial(int sz, int a[]) : 多項係数 $\frac{(a[0]+a[1]+ \cdots + a[sz-1])!}{a[0]! a[1]! \cdots a[sz-1]!}$ を返します
- T comb.Multinomial(int a) : 1 を返します
- T comb.Multinomial(int a, int b) : $\frac{(a+b)!}{a! b!}$ を返します
- T comb.Multinomial(int a, int b, int c) : $\frac{(a+b+c)!}{a! b! c!}$ を返します
- T comb.Multinomial(int a, int b, int c, int d) : $\frac{(a+b+c+d)!}{a! b! c! d!}$ を返します
- T comb.Catalan(int n) : カタラン数 $\frac{(2n)!}{n! (n+1)!}$ を返します
- T comb.C_s(ll a, ll b) : 二項係数 C(a,b) を前計算する階乗を使用せずに愚直に計算します.時間計算量は $O( \min (b, a-b) )$ 程度です.
- T comb.P_s(ll a, ll b) : P(a,b) を前計算する階乗を使用せずに愚直に計算します.時間計算量は $O(b)$ 程度です.
- T comb.per_s(ll n, ll k) : 整数 n をちょうど k 個の正整数の和に分割する場合の数(k以下の整数の和に分割する場合の数)を求めます.現在 k は 3 以下の場合にのみ対応しています.時間計算量は $O(k!)$ 程度です.
- T comb.dfac(int k) : 二重階乗 $k!!$ を返します.k が負の奇数の場合も動きますが,現状手抜きで毎回逆数の計算が入るので前計算除いても O(1) ではないです.
- T comb.pw2(int k) : $2^k$ を返します.k が負の場合も動きます.$\min(0,k) \leq i \leq \max(0,k)$ なる $i$ に対して $2^i$ を全て計算するので,$|k|$ が大きいとき,何回も呼び出さない場合は ** 演算子などを利用するほうが良いです.
- T comb.pw3(int k) : $3^k$ を返します.
- T comb.pw10(int k) : $10^k$ を返します.
- T comb.repunit(int k) : $(10^k-1) / 9$ を返します.これは 1 が k 個連続する値です.k は非負の整数でないといけません.
使わなさそうなの:変数
- int mem_fact : 階乗,階乗の逆数をどれだけメモリ確保して前計算しているか
- T *factri : 前計算した階乗の値 factri[i] = i!
- T *ifactri : 前計算した階乗の逆数値 factri[i] = 1/i!
使わなさそうなの:関数
- Comb() : コンストラクタ(メモリ確保状況 mem_fact などを0に設定)
- void expand_fact(int k) : 階乗と階乗の逆数の 0! から (k-1)! まで(もしくはそれ以上に)前計算をする.既にk以上が前計算されていれば何もしない
combination_mint は階乗とその逆数を前計算することで二項係数を高速に求めます.mint型で答えを返します.
構造体を使用しているので combination_mint comb; と最初に宣言して下さい.
- comb.init(int n, void **mem = &wmem) : 階乗とその逆数をを 0 乗から n-1 乗まで計算します.ワーキングメモリを使用します.
- mint comb.fac[] : fac[i] に i の階乗を格納しています.
- mint comb.ifac[] : ifac[i] に i の階乗の逆元を格納しています.
- mint comb.C(int a, int b) : C(a,b) = $\frac{a!}{b!(a-b)!}$ を返します(bが負,a以上のときは0を返します).aはnより小さい必要があります.
- mint comb.P(int a, int b) : P(a,b) = $\frac{a!}{(a-b)!}$ を返します.aはnより小さい必要があります.
- mint comb.H(int a, int b) : H(a,b) = C(a+b-1, b) を返します.a+b-1はnより小さい必要があります.
combination_mint comb;
mint res;
comb.init(101); // 100の階乗までを計算.以降 C(100,hoge) まで計算可能
res = comb.C(5,2);
wt(res); // 10 = 5*4/2 = 5個から2個を選ぶ選び方
- reduceFraction(a,b) : aとbをgcd(a,b)で割ります(分数a/bを約分します).
int a = 32, b = 24;
reduceFraction(a,b);
wtF("{a}/{b}\n"); // 4/3 と表示
- int fibonacci_mod(ull n, int md), int fib_mod(ull n, ind md) : フィボナッチ数の第 n 項を mod md で求めます.mdを省略するとmd = MDが指定されます.
int i, f[10];
rep(i,10) f[i] = fib_mod(i);
wt(f(10)); // 0 1 1 2 3 5 8 13 21 34
- template<class T> inline int Cmod2(T n, T k) : C(n,k) mod 2 を返す.
- template<class T> inline int Digit(T n) : 正の整数nを10進数で書いたときの桁数を返す(0に対しては現状0を返す)
- template<class T, class S> inline int Digit(T n, S b) : 正の整数nをb進数で書いたときの桁数を返す
- template<class T, class S> inline int Digit(T n, S res[]) : 戻り値として正の整数nを10進数で書いたときの桁数を返し,res[i]に10^iの桁の数値を代入する
- template<class T, class S, class U> inline int Digit(T n, S res[], U b) : 戻り値として正の整数nをb進数で書いたときの桁数を返し,res[i]にb^iの桁の数値を代入する
- template<class T, class S> inline void DigitF(T n, int sz, S res[]) : nを10進数で書いたときの下sz桁を返す.sz桁未満の場合は0で埋める.
- template<class T, class S, class U> inline void DigitF(T n, int sz, S res[], U b) : nをb進数で書いたときの下sz桁を返す.sz桁未満の場合は0で埋める.
- template<class T> inline T invDigit(int sz, T d[]) : sz桁の10進数d[]の値を返します.sz=4, d[]={4,6,1,0}のとき164を返す.
- template<class T, class S> inline T invDigit(int sz, T d[], S b) : sz桁のb進数d[]の値を返します.
- template<class T> inline int sod(T n) : nを10進数で書いたときの各桁の和を返す
- template<class T, class S> inline S sod(T n, S b) : nをb進数で書いたときの各桁の和を返す
- b[t](a,b,c):t進数でabcの値を表す.これは (((a)*(t)+(b))*(t)+(c)) に展開されます.
- b[x,y](a,b,c,d):((a*x+b)*x+c)*y+d を表す.[]および()の中の引数の数は任意.下の桁から y,x,x,x,... パターンの数を取るような進数を考えている感じ.
- template<class T> int LIS_length(int n, T a[], void *mem = wmem) : 要素数nの配列aの最長増加部分列の長さを返す
- template<class T> int weaklyLIS_length(int n, T a[], void *mem = wmem) : 要素数nの配列aの最長非減少部分列の長さを返す
- template<class T> int coordcomp(int n, T arr[], int res[] = NULL, void *mem = wmem) : 座標圧縮.arrの各要素の大きさの順番を維持したまま小さな非負整数に変換したものをresに格納.res = NULLのときはarrに上書き.異なる要素の数を戻り値として返す.coordcomp()の代わりにcoord_comp()も可能.$O(n \log n)$ 時間.
- template<class T> int coordcomp(int n1, T arr1[], int n2, T arr2[], int res1[] = NULL, int res2[] = NULL, void *mem = wmem) : 座標圧縮.2つの配列を連結してから処理する.異なる要素の数を戻り値として返す.coordcomp()の代わりにcoord_comp()も可能.
int n;
int C[5] = {1000, 100, 1, 8, 100};
n = coordcomp(4, C);
wt(n); // 4
wt(C(5)); // 3 2 0 1 2
int A[5] = {100, 4, 8, 4, 100};
int B[4] = {8, 2, 1200, 8};
int rA[5], rB[4];
n = coordcomp(5, A, 4, B, rA, rB);
wt(n); // 5
wt(A(5)); // 100 4 8 4 100
wt(B(4)); // 8 2 1200 8
wt(rA(5)); // 3 1 2 1 3
wt(rB(4)); // 2 0 4 2
次元圧縮.多次元のグリッドでのBFSなどに.
構造体で,[x]次元のものは dimcomp[x] c; で定義したとする.2次元~5次元まで.
2次元 dimcomp2 c; で定義.
- dimcomp2(){} : コンストラクタ.
- dimcomp2(int b) : コンストラクタ.c.set(b); と同じ.
- dimcomp2(int a, int b) : コンストラクタ.c.set(a,b); と同じ.
- inline void c.set(int b) : (x,y) で y は 0 以上 b 未満を動くと設定する.((x,y) は x*b+y に対応させることにする)
- inline void c.set(int a, int b) : (x,y) で x は 0 以上 a 未満を,y は 0 以上 b 未満を動くと設定する.実は a は使ってないが.
- inline int c.mask(int a, int b) : (a,b) に対応する値を返す.
- inline int operator()(int a, int b) : c.mask(a,b) と同じ.
- inline void para(int mask, int &a, int &b) : mask に対応する座標を a, b に代入する.
- inline void operator()(int mask, int &a, int &b) : c.para(mask, a, b) と同じ.
3次元 dimcomp3 c; で定義.
- dimcomp3(){} : コンストラクタ.
- dimcomp3(int b, int c) : コンストラクタ.c.set(b,c); と同じ.
- dimcomp3(int a, int b, int c) : コンストラクタ.c.set(a,b,c); と同じ.
- inline void c.set(int b, int c) : (x,y,z) で y は 0 以上 b 未満を,z は 0 以上 c 未満を動くと設定する.((x,y,z) は x*b*c+y*c+z に対応させることにする)
- inline void c.set(int a, int b, int c)
- inline int c.mask(int a, int b, int c)
- inline int operator()(int a, int b, int c)
- inline void para(int mask, int &a, int &b, int &c)
- inline void operator()(int mask, int &a, int &b, int &c)
4次元 dimcomp4 c; で定義.
5次元 dimcomp5 c; で定義.
int dx, dy, mask;
dimcomp2 c(20,15); // size = 20 x 15 (dimcomp2 c(15) でもほぼ同じ)
dx = 3; dy = 9;
mask = c(dx,dy);
wt(mask); // 3 * 15 + 9 = 54
mask = c(dx+1, dy);
wt(mask); // 4 * 15 + 9 = 69
c(mask,dx,dy);
wt(dx,dy); // dx = 4, dy = 9
- template<class T> void Unique(int &N, T A[], int sorted=0) : 要素数Nの配列Aの重複する要素を削除する.要素数,配列は書き換えられる.勝手にソートする.既にソート済みならsorted=1にするとソートしなくなり速くなる.
- template<class T, class S> void Unique(int &N, T A[], S B[], int sorted=0) : 上と同じ.A[i]はB[i]個あると思って,要素の数もカウントする.実際は個数じゃないのでB[i]は整数値じゃなくても良い.
{
int N = 7;
int A[7] = {7,4,1,4,1,4,1};
Unique(N,A);
// N = 3, A = {1, 4, 7}
}
{
int N = 7;
int A[7] = {7,4,1,4,1,4,1};
int B[7] = {1,2,2,2,2,2,2};
Unique(N,A,B);
// N = 3
// A = {1, 4, 7}
// B = {6, 6, 1}
}
twoMultisets構造体.
格納するデータの型をTとして twoMultisets<T> s; で定義します.格納している要素数を N と書くことにします.
中身は2つのmultisetからなっていて,小さい方のいくつかと大きい方のいくつかを格納しています.
K番目の値を知りたい場合は,小さい方K個と大きい方N-K個を格納するようにする,などと適当に要素を移動させてクエリに答えます.
Kが固定,Kが常にN/2,Kが単調に動く,などの場合に効率的にクエリに答えることができると予想されます.
和を求めることもできますが,和も T 型で格納できる必要があります.
名前の由来は twopointers っぽい動き方しているような気がしたから.
- void s.clear() : 中身を空にします
- void s.insert(T x) : x を 1 個挿入します
- int s.erase(T x) : x を 1 個削除します.削除したら 1 を,その要素が存在しなくて削除できなかったら 0 を返します
- int s.size() : 要素数 N を返します
- T s.allsum() : 中身の全ての要素の和を返します
- T s.Kth(int K) : 小さい方から K+1 番目(0番目から数えるとK番目)の要素の値を返す(0 ≤ K < N である必要がある)
- T s.Ksum(int K) : 小さい方から K 個の要素の和を返す(0 ≤ K ≤ N である必要がある)
- T s.rKth(int K) : 大きい方から K+1 番目(0番目から数えるとK番目)の要素の値を返す(0 ≤ K < N である必要がある)
- T s.rKsum(int K) : 大きい方から K 個の要素の和を返す(0 ≤ K ≤ N である必要がある)
- T s.getMin() : 要素の中で最小値を返します(空の場合は何を返すか不明です)
- T s.getMin(T x) : 要素の中で最小値を返します(空の場合は x を返します)
- T s.getMax() : 要素の中で最大値を返します(空の場合は何を返すか不明です)
- T s.getMax(T x) : 要素の中で最大値を返します(空の場合は x を返します)
使わないもの
- twoMultisets() : コンストラクタ.clear() 呼び出すだけ
- void s.assign(int K) : 小さい方の要素を格納するmultisetのサイズがKになるように調整する
- multiset<T> s.a : 小さい方の要素を格納するmultiset
- multiset<T> s.b : 大きい方の要素を格納するmultiset(常に任意のaの要素より任意のbの要素の方が大きいか等しい,が成り立つように管理されている)
- T s.sa : 小さい方の要素を格納するmultisetに含まれる要素の和
- T s.sb : 大きい方の要素を格納するmultisetに含まれる要素の和
- ll inversion_range(int N, int A[], int mn, int mx, void *mem=wmem) : 要素数Nの配列A[]の転倒数を求めます.Fenwick Treeを使うやつ.配列A[]の要素の最小値はmn以上,最大値はmx以下でなければなりません.時間計算量は $O(N \log (mx-mn) + (mx-mn))$ 程度で,$mx-mn \approx N$ ならば以下のinversionより高速なはずです.
- template<class T> ll inversion(int N, T A[], void *mem=wmem) : 要素数Nの配列A[]の転倒数を求めます.マージソートしながら数えるやつ.時間計算量は $O(N \log N)$ です.
{
int d[10] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
int inv;
inv = inversion_range(10, d, 1, 9);
wt(inv); // 14
}
{
double d[10] = {1e3, 1e1, 1e4, 1e1, 1e5, 1e9, 1e2, 1e6, 1e5, 1e4};
int inv;
inv = inversion(10, d);
wt(inv); // 14
}
- template<class T, class S> void ZetaTransform(int N, T A[], S res[]) : 高速ゼータ変換します.resがNULLのときは結果を配列Aに上書きします.時間計算量は $O(N \log N)$.
- template<class T> void ZetaTransform(int N, T A[]) : 高速ゼータ変換します.結果を配列Aに上書きします.
- template<class T, class S> void MoebiusTransform(int N, T A[], S res[]) : 高速メビウス変換します.resがNULLのときは結果を配列Aに上書きします.時間計算量は $O(N \log N)$.型Sは型Tの値を表せる必要があります(結果がSに収まるからってのは駄目).
- template<class T> void MoebiusTransform(int N, T A[]) : 高速メビウス変換します.結果を配列Aに上書きします.
非負整数 x, y を2進数で書いたとき,xの1の桁がyの1の桁に包含されているとき,xはyに含まれると呼ぶことにする.
ZetaTransform は res[i] は jがiに包含されている整数を動くとき A[j] の和が代入されます.
メビウス変換はゼータ変換の逆変換です.
int A[6] = {2,5,9,4,1,8}, B[6], C[6];
ZetaTransform(6, A, B);
MoebiusTransform(6, B, C);
rep(i,6) printf("%d %d%d%d %02d %02d %02d\n", i, (i>>2)%2, (i>>1)%2, i%2, A[i], B[i], C[i]);
// 0 000 02 02 02
// 1 001 05 07 05
// 2 010 09 11 09
// 3 011 04 20 04
// 4 100 01 03 01
// 5 101 08 16 08
res[i] は jがiに包含されている整数を動くとき A[j] の最小値 | 最大値を求めるのは以下の関数です.
小さい方から2個,3個求めるものもあります.この場合は,要素数が足りない場合は,numeric_limits<S>::max();が代入されます.
- template<class T, class S> void ZetaTransform_min(int N, T A[], S res[])
- template<class T> void ZetaTransform_min(int N, T A[]) : 結果はAに上書き
- template<class T, class S> void ZetaTransform_min(int N, T A[], S r1[], S r2[])
- template<class T, class S> void ZetaTransform_min(int N, T A[], S r1[], S r2[], S r3[]) : r1に最小値,r2に小さい方から2番目,r3に小さい方から3番目が代入されます.
- template<class T, class S> void ZetaTransform_max(int N, T A[], S res[])
- template<class T, class S> void ZetaTransform_max(int N, T A[]) : 結果はAに上書き
- int runLength(int N, T A[], T val[] = NULL, int len[] = NULL) : 長さNの配列Aをランレングス圧縮します.要素 val[i] が len[i] 個続くという感じに求めます.配列 val, len の要素数を返します.val, len が不要な場合は NULL を指定します(内部的にはval, len の引数の型は void * 型になっていてキャストせず NULL で通りますが,NULLでない場合に型が間違っていても通るので注意).
int N = 10;
int A[10] = {0,0,1,1,1,0,0,3,2,1}; // 0が2個 - 1が3個 - 0が2個 - 3が1個 - 2が1個 - 1が1個
int e, val[10], len[10];
e = runLength(N,A,val,len);
wt(e); // 6 と表示されます
wt(val(e)); // 0 1 0 3 2 1
wt(len(e)); // 2 3 2 1 1 1
- template<class T> T popFirst(multiset<T> &a), template<class T> T popFirst(set<T> &a) : aの最初の要素を削除し,その要素の値を返す
- template<class T> T getFirst(multiset<T> &a), template<class T> T getFirst(set<T> &a) : aの最初の要素の値を返す
- template<class T> T popLast(multiset<T> &a), template<class T> T popLast(set<T> &a) : aの最後の要素を削除し,その要素の値を返す
- template<class T> T getLast(multiset<T> &a), template<class T> T getLast(set<T> &a) : aの最後の要素の値を返す
いずれの関数も,空のmultiset, setを引数に取ってはいけません.
- template<class T> inline int isLeapYear(const T y) : y年がうるう年のときに1,そうじゃないときに0を返す.T = int, ll あたりを想定.
- inline int numOfDaysInMonth(const int m) : うるう年じゃない年のm月の日数を返す
- template<class T> inline int numOfDaysInMonth(const T y, const int m) : y年m月の日数を返す
- template<class T> inline int dayOfWeek(T y, int m, int d) : 曜日を返す.0は月曜,1は火曜,…,6が日曜.ツェラーの公式を用いる.
- inline const char* dayOfWeekStr(int w) : 0~6の曜日に対応する文字列を返す.0~6までは "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" に対応.
- template<class T> inline const char* dayOfWeekStr(T y, int m, int d) : 曜日に対応する文字列を返す
- template<class T> inline void prevDay(T &y, int &m, int &d) : y年m月d日の1日前の日付に y, m, d を変更する
- template<class T> inline void nextDay(T &y, int &m, int &d) : y年m月d日の1日後の日付に y, m, d を変更する
- inline ll dayIndex(ll y, int m, int d) : 1年1月1日を1日目として,y年m月d日が何日目かを返す
- template<class S, class T> void dayFromIndex(S ind, T &y, int &m, int &d) : 1年1月1日を1日目として,ind 日目の日付 y 年 m 月 d 日を返す(y, m, d に代入する)
基本的にグレゴリオ暦を仮定
- inline int isVowel(const char c) : 母音のとき1,そうじゃないときに0を返す(母音はaiueoでyは含まない)
- template<class T, class S> int KMP(T A[], int As, T B[], int Bs, S res[], int *fail = (int*)wmem) : KMP法.AsはAの配列の長さ,BsはBの配列の長さ.Aの連続する部分列として何個Bがあるかを返す.res[i]=1はA[i]から始まる連続する部分文字列がBであるという意味.failは内部で利用するが,例えば,Bs-fail[Bs]は配列Bの周期になっているなどの利用価値がある.配列Bの周期がpであるとはB[i]=B[i-p]が全ての p ≤ i < Bs で成り立つこと(pはBsの約数でなくても良い).
- template<class T> int KMP(T A[], int As, T B[], int Bs, int res[] = NULL, int *fail = (int*)wmem) : resを省略するとき用.KMP(A,As,B,Bs) で呼び出し可能.
- template<class T> int isSubsequence(int As, T A[], int Bs, T B[]) : 配列Bが配列Aの部分列ならば1,そうでなければ0を返す.
- int isSubstring(string A, string B, void *mem = wmem) : 文字列Bが文字列Aの部分文字列ならば1,そうでなければ0を返す.内部ではKMP法を利用.
- template<class T> inline int isPalindrome(const int N, const T A[]) : 長さNの配列Aが回文(A[i]==A[N-1-i])ならば1を,そうでないなら0を返す.
- template<class T> int longestSuffixPrefix(int As, T A[], int Bs, T B[], void *mem = wmem) : KMP法で「配列Aのsuffix = 配列Bのprefix」となる最大の長さを返す.
- string strReplace(string str, string bef, string aft, void *mem = wmem) : str に現れる部分文字列 bef を前から順番に aft に置き換えてできる文字列を返す.置き換え後の文字は bef にマッチしない.KMP法を使用.
- string strReplace(string str, vector<string> bef, vector<string> aft, void *mem = wmem) : str に現れる部分文字列 bef[k] を前から順番に aft[k] に置き換えてできる文字列を返す.置き換え後の文字は bef にマッチしない.KMP法を使用.
int A[10] = {1,2,3,1,2,3,1,2,3,1};
int B[4] = {1,2,3,1};
int res[10], cnt;
cnt = KMP(A, 10, B, 4, res);
wt(cnt); // 3
wt(res(10)); // 1 0 0 1 0 0 1 0 0 0
- template<class T> void SuffixArray(T *s, int N, int K, int *SA, int *LCP = NULL, void *mem = wmem) : 配列sのSuffixArrayを求める.sの長さはNで,sの各要素は1以上K-1以下である必要がある.LCPも必要であれば求めることができる.末尾に0を付加して計算するため,配列s,SA,LCPはN+1要素以上メモリが確保されていないといけない.時間計算量は $O(N+K)$.Kが大きかったり,0以下の要素を含む場合などは先に座標圧縮を行う.
char S[12] = "abracadabra";
int SA[12], LCP[12];
SuffixArray(S, 11, 128, SA, LCP);
rep(i,12) printf("%4d %4d %4d %s\n", i, SA[i], LCP[i], S+SA[i]);
/*
0 11 -1
1 10 0 a
2 7 1 abra
3 0 4 abracadabra
4 3 1 acadabra
5 5 1 adabra
6 8 0 bra
7 1 3 bracadabra
8 4 0 cadabra
9 6 0 dabra
10 9 0 ra
11 2 2 racadabra
*/
Arr1d 構造体.
累積和を計算しておけば連続する部分列の和を計算できたりするような,1回準備したら(配列の中身を変更しなければ)簡単に答えられるようなクエリに対応するための型です.
(計算状況リセットしてから)最初の1回のみ,連続部分列の和を取得しようとしたときに累積和を計算するような感じ.
以下は,Arr1d<T> a;を想定.
[特殊な話]
int n; Arr1d<ll> a;
rd(n, a(n));
と読み込むことができます.
rd(a(n));で読み込んだときには,読み込む直前に a.malloc(n); が走って勝手にメモリ確保と配列サイズが指定されます.
a[0], a[1], ..., a[n-1] で普通に要素にアクセスできます.
[コンストラクタ・メモリ関係・初期化など]
- Add1d() : コンストラクタ.変数の初期がぐらいしかしません.
- Add1d(int nn) : コンストラクタ.長さnnの配列としてメモリ確保もします.
- ~Add1d() : デストラクタ.メモリの開放などをします.
- void a.constructor(), void a.constructor(int nn) : コンストラクタと同じ内容を強制的に呼び出す.
- void a.destructor(), void a.free() : デストラクタと同じ内容を強制的に呼び出す.
- void a.reset() : リセット.全ての機能において前計算をしていない状況にします.配列の中身をいじったら呼び出すと良いです.
- void a.malloc(int nn) : n個のデータの領域を確保し(すでにn以上のメモリを確保している場合は何もしません),配列長をnnに変更します.リセットされます.
- void a.setN(int nn) : mallocと同じです.
- void a.set(vector<T>) : それにします.メモリも自動で確保します.リセットされます.
- void a.set(int nn, T a[]) : それにします.メモリも自動で確保します.リセットされます.
[累積和関係]
- T a.getSum(int i, int j) : a[i]+a[i+1]+...+a[j] を返します.j=i-1の場合は0を返します.前計算O(n),各クエリO(1).
- void a.setSum() : 基本的に使わない.累積和を強制的に(再)計算します.
[縦横に連続する数]
- int a.ConstLenLeft(int st, T val) : a[st]から左に向かってvalが何個連続しているかを返します.a[st] = a[st-1] = val, a[st-2] != val なら 2,a[st] != val なら 0を返します.stは0以上n未満である必要があります.前計算O(n),各クエリO(1).
- int a.ConstLenLeft(int st) : ConstLenLeft(st, a[st]) と同じです.
- int a.ConstLenLeftCyclic(int st, T val) : 配列aがサイクリック(a[-1] = a[n-1])として ConstLenLeft(st, val) を返します.stは負やn以上でも mod n で扱われます.答えが無限になる場合は int_inf を返します.
- int a.ConstLenLeftCyclic(int st)
- int a.ConstLenRight(int st, T val) : a[st]から右に向かってvalが何個連続しているかを返します.
- int a.ConstLenRight(int st)
- int a.ConstLenRightCyclic(int st, T val)
- int a.ConstLenRightCyclic(int st)
- void a.setConstLenLeft() : 基本的に使わない.ConstLenLeft() 系の強制的に前計算をします.
- void a.setConstLenRight() : 基本的に使わない.ConstLenRight() 系の強制的に前計算をします.
[ヒストグラム(密な場合)]
- int a.dHist(T x) : 配列a[]の中にxが登場する回数を返す.前計算 O(max(a)-min(a)),各クエリ O(1).
- void a.setDHist() : 基本的に使わない.
[その他]
- void a.sort(void) : 配列をソートします.リセットされます.
[基本的に使わない情報]
- int a.n : 配列長
- int a.mem : 配列のメモリを確保している長さ
- T *a.d : 生配列
Grid1d構造体.
Grid1d<int> g; などで定義し,g.malloc(n); でメモリを確保する.メモリ解放するときは g.free(); .
すると,g[i]が使えるようになり,それぞれint型.
g[i] にデータを代入して,g.setSum(),g.setDir() 等で累積和などのテーブルを作成して,利用する.
データを変更した場合は,g.setSum() などを再度呼び出せば良い.
行の数,列の数を変更する場合は,現状,g.free()→g.malloc(n)とやり直すしかない.
以下は,Grid1d<T> g;を想定.
- void g.malloc(const int n) : n固のデータ領域を確保(g[0]からg[n-1]まで使える)
- void g.free(void) : メモリ解放(以下の機能で確保したメモリも全て解放)
累積和関係
- void g.setSum(void) : 累積和のテーブルを作成(以下の機能を使う前に呼び出す)
- inline T g.getSum(const int a, const int b) : g[i] (a ≤ i ≤ b) の和を返す.ただし,a > b の場合は正しく動かない
- T *g.d_s : g.d_s[i] には g.d[0]+...+g.d[i-1] が入っている.
縦横に連続する数
- void g.setDir(void) : 以下の up, dw, lf, rg をセットする.g[i]と同じ値が左右に何個続くかを求める
- void g.setDirMatch(const T v) : 以下の up, dw, lf, rg をセットする.g[i]がvとなるようなセルが上下左右に何個続くかを求める
- int *lf : lf[i] は g[i], g[i-1], ..., g[i-k+1] が全て条件を満たすような最大のkが入っている(自分のマスから左方向に連続して何マスが条件を満たすか)
- int *rg : rg[i] は g[i], g[i+1], ..., g[i+k-1] が全て条件を満たすような最大のkが入っている(自分のマスから右方向に連続して何マスが条件を満たすか)
- int *up : rgと同じ.
- int *dw : lfと同じ.
Grid2d構造体.
Grid2d<int> g; などで定義し,g.malloc(r,c); でメモリを確保する.メモリ解放するときは g.free(); .
すると,g[i][j]が使えるようになり,それぞれint型.
g[i][j] にデータを代入して,g.setSum(),g.setDir() 等で累積和などのテーブルを作成して,利用する.
データを変更した場合は,g.setSum() などを再度呼び出せば良い.
行の数,列の数を変更する場合は,現状,g.free()→g.malloc(r,c)とやり直すしかない.
以下は,Grid2d<T> g;を想定.
- void g.malloc(const int r, const int c) : r行c列のデータ領域を確保(g[0][0]からg[r-1][c-1]まで使える)
- void g.free(void) : メモリ解放(以下の機能で確保したメモリも全て解放)
累積和関係
- void g.setSum(void) : 累積和のテーブルを作成(以下の機能を使う前に呼び出す)
- inline T g.getSum(const int r1, const int c1, const int r2, const int c2) : g[i][j] (r1 ≤ i ≤ r2, c1 ≤ j ≤ c2) の和を返す.ただし,r1 > r2 または c1 > c2 の場合は正しく動かない
- T **g.d_s : g.d_s[i][j] には g.d[0][0]+...+g.d[i-1][j-1] が入っている.
縦横に連続する数
- void g.setDir(void) : 以下の up, dw, lf, rg をセットする.g[i][j]と同じ値が上下左右に何個続くかを求める
- void g.setDirMatch(const T v) : 以下の up, dw, lf, rg をセットする.g[i][j]がvとなるようなセルが上下左右に何個続くかを求める
- int **up : up[i][j] は g[i][j], g[i-1][j], ..., g[i-k+1][j] が全て条件を満たすような最大のkが入っている(自分のマスから上方向に連続して何マスが条件を満たすか)
- int **dw : dw[i][j] は g[i][j], g[i+1][j], ..., g[i+k-1][j] が全て条件を満たすような最大のkが入っている(自分のマスから下方向に連続して何マスが条件を満たすか)
- int **lf : lf[i][j] は g[i][j], g[i][j-1], ..., g[i][j-k+1] が全て条件を満たすような最大のkが入っている(自分のマスから左方向に連続して何マスが条件を満たすか)
- int **rg : rg[i][j] は g[i][j], g[i][j+1], ..., g[i][j+k-1] が全て条件を満たすような最大のkが入っている(自分のマスから右方向に連続して何マスが条件を満たすか)
その他
- template<class S> inline void g.getDist4(int sr, int sc, S **res, void *mem = wmem) : 上下左右の4方向に進めるとして,(sr,sc) から各マス (i,j) への最短移動コストを res[i][j] に代入する.ただし,コストは始点終点を含む全ての通ったマスの g[i][j] の和で,g[i][j] の値が負なら通行不能とする.res[i][j]が負なら辿り着けない.
- template<class S> inline void g.getDist4_BFS(int sr, int sc, S **res, void *mem = wmem) : getDist4()とほぼ同様だが g[i][j] の値がマイナスなら通行不可,そうでなければ全てコスト1とする(通るマスの数の最小化).
ビットに関すること.
- BIT_ith(i) : (1<<(i)) に展開されます.ただし,iが(変数ではなくベタな)整数で31以上の場合は (1LL<<(i)) に展開します.
- BIT_ith(i,j) : ((i)&(1<<(j))) に展開されます.ただし,jが(変数ではなくベタな)整数で31以上の場合は ((i)&(1LL<<(j))) に展開します.iのjビット目を取り出す.
- BIT_lowest(i) : (-(i) & (i)) に展開されます.
- BIT_nonlowest(i) : ((i) & ((i)-1)) に展開されます.
- inline int BIT_popcount(const int x) : __builtin_popcount(x) の値を返します.1のビットの個数.現状,gcc以外のコンパイラだと多分動かないので注意.
- inline int BIT_popcount(const ll x) : __builtin_popcountll(x) の値を返します.1のビットの個数.現状,gcc以外のコンパイラだと多分動かないので注意.
- inline int BIT_ctz(const int x) : __builtin_ctz(x) の値を返します.下何桁のビットが0なのか.現状,gcc以外のコンパイラだと多分動かないので注意.
- inline int BIT_ctz(const ll x) : __builtin_ctzll(x) の値を返します.下何桁のビットが0なのか.現状,gcc以外のコンパイラだと多分動かないので注意.
FFTを用いて畳み込みを行います.
誤差がそれなりにのるので使用する際には注意.
- void convolution(double A[], int As, double B[], int Bs, double res[], int Rs, void *mem = wmem) : res[k] = A[0]*B[k] + A[1]*B[k-1] + ... + A[k]*B[0] を k = 0, 1, ..., Rs-1 まで計算します.AsとBsは配列A, Bの長さです.
- void convolution(double A[], int As, double res[], int Rs, void *mem = wmem) : res[k] = A[0]*A[k] + A[1]*A[k-1] + ... + A[k]*A[0] を k = 0, 1, ..., Rs-1 まで計算します.Asは配列Aの長さです.
FFTを用いて畳み込みを行います.
mod の値は 2冪+1 でなければならないなどの条件が存在します.
root には mod の原始根のうちの1つを指定してください.defineしたMDが素数の場合,その原始根は MD_PRIMITIVE_ROOT で勝手にdefineされます.
- void convolution(mint A[], int As, mint B[], int Bs, mint res[], int Rs, mint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(mint A[], int As, mint res[], int Rs, mint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(Mint A[], int As, Mint B[], int Bs, Mint res[], int Rs, Mint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(Mint A[], int As, Mint res[], int Rs, Mint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(modint A[], int As, modint B[], int Bs, modint res[], int Rs, modint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(modint A[], int As, modint res[], int Rs, modint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(Modint A[], int As, Modint B[], int Bs, Modint res[], int Rs, Modint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
- void convolution(Modint A[], int As, Modint res[], int Rs, Modint root = MD_PRIMITYVE_ROOT, void *mem = wmem)
double a[3] = {1,2,3}, b[3] = {1,2,3}, c[6];
convolution(a, 3, b, 3, c, 6);
wt(c(6)); // 0.999999999999999 3.999999999999999 10.000000000000000 12.000000000000000 9.000000000000000 0.000000000000001
convolution(a, 3, c, 6);
wt(c(6)); // 0.999999999999999 3.999999999999999 10.000000000000000 12.000000000000000 9.000000000000000 0.000000000000001
グラフの隣接リスト(隣接配列?)のようなものを作ります.
時間ごとにイベントを処理する場合などの整理などにも多分便利.
- template<class S> void wAdjEdge(const int N, const int M, const int *A, const S *B, int **res_sz, S ***res_B, void **mem = &wmem)
- template<class S, class T> void wAdjEdge(const int N, const int M, const int *A, const S *B, const T *C, int **res_sz, S ***res_B, T ***res_C, void **mem = &wmem)
- template<class S, class T, class U> void wAdjEdge(const int N, const int M, const int *A, const S *B, const T *C, const U *D, int **res_sz, S ***res_B, T ***res_C, U ***res_D, void **mem = &wmem)
- template<class S, class T, class U, class V> void wAdjEdge(const int N, const int M, int *A, const S *B, const T *C, const U *D, const V *E, int **res_sz, S ***res_B, T ***res_C, U ***res_D, V ***res_E, void **mem = &wmem)
int A[5] = {1,1,3,3,0}, B[5] = {10,30,30,30,50};
int *es, **edge;
wAdjEdge(4, 5, A, B, &es, &edge);
rep(i,4) wt(i, ':', es[i], ':', edge[i](es[i]));
// 0 : 1 : 50
// 1 : 2 : 10 30
// 2 : 0 :
// 3 : 2 : 30 30
ハンガリアン法.n ≤ m と仮定.
mat[0][c[0]] + mat[1][c[1]] + ... + mat[n-1][c[n-1]] の最小値を求める.ただし,c[i]は互いに異なる(c[i]は0以上m未満).
それを達成する配列cはmatch[]に代入して返す.matchにNULLにしておくと最小値だけ返す.
- template<class T> T Hungarian(T **mat, int n, int m, int match[] = NULL, void *mem = wmem)
多項式補間(ラグランジュ補間).
f(x[i]) = y[i] (i=0,1,...,n-1)となる n-1 次以下の多項式 f を考える.
- template<class T> T polationVal(int n, T x[], T y[], T t) : f(t) の値を返す.$O(n^2)$ 時間ぐらい.
- template<class T> T polationVal(int n, T y[], T t) : f(t) の値を返す.ただし x[i] = i とする.(T型の演算が $O(1)$ だと思って)$O(n)$ 時間ぐらい.
- template<class T> Polynomial<T> polationPoly(int n, T x[], T y[]) : fを返す.$O(n^2)$ 時間ぐらい.
- vector<string> Explode(const string &str, const string &d) : str を d で区切って返します
- string Implode(const vector<string> &v, const string &d) : v を d で連結します
効率的な実装にはなっていない(遅い)です.
Explode("hoge,piyo,,123,", ",") は {"hoge", "piyo", "", "123", ""} を返します.
Explode("hoge,,,piyo,,123,", ",,") は {"hoge", ",piyo", "123,"} を返します.
v = {"hoge", "piyo", "", "123", ""} のとき Implode(v, "-") は "hoge-piyo--123-" を返します.
整数の平方根.(ただし n として ll でオーバーフロー直前みたいなのを引数に与えると動かない可能性が高い)
- inline ll Isqrt_f(const ll n) : 非負整数 n に対して,$\lfloor \sqrt{n} \rfloor$ の値($n$の平方根,小数点以下切り捨て)を返す.f は floor のつもり.
- inline ll Isqrt_c(const ll n) : 非負整数 n に対して,$\lceil \sqrt{n} \rceil$ の値($n$の平方根,小数点以下切り上げ)を返す.c は ceil のつもり.
- inline ll Isqrt_s(const ll n) : 非負整数 n に対して,$x^2 = n$ なる非負整数 x の値を返す.ただし,存在しないなら -1 を返す.s は strict のつもり.
- inline ll Isqrt(const ll n) : Isqrt_f と同じ.
整数の立方根.
現在は非負整数に対応だが,将来的に負の整数に対応する可能性がある.その場合,Icbrt_s(-8) などは答えが変わる可能性があるので注意.
- inline ll Icbrt_f(const ll n) : 非負整数 n に対して,$\lfloor \sqrt[3]{n} \rfloor$ の値($n$の平方根,小数点以下切り捨て)を返す.f は floor のつもり.
- inline ll Icbrt_c(const ll n) : 非負整数 n に対して,$\lceil \sqrt[3]{n} \rceil$ の値($n$の平方根,小数点以下切り上げ)を返す.c は ceil のつもり.
- inline ll Icbrt_s(const ll n) : 非負整数 n に対して,$x^3 = n$ なる非負整数 x の値を返す.ただし,存在しないなら -1 を返す.s は strict のつもり.
- inline ll Icbrt(const ll n) : Icbrt_f と同じ.
整数のlog2.
内部で __builtin_clz や __builtin_ffs 系列の関数を使用しています.GCC系以外では動かないかもしれません.
入力が 0 以下なら -1 を返します.
Ilog2の最初の文字は大文字のアイ,2文字目は小文字のエルです.
- inline int Ilog2_f(const int n) : $\lfloor \log_2 n \rfloor$ を返します.最も1の上の桁の場所を返す(一番下の桁を0桁目のカウントして).
- inline int Ilog2_f(const ll n) : $\lfloor \log_2 n \rfloor$ を返します.最も1の上の桁の場所を返す(一番下の桁を0桁目のカウントして).
- inline int Ilog2_c(const int n) : $\lceil \log_2 n \rceil$ を返します.
- inline int Ilog2_c(const ll n) : $\lceil \log_2 n \rceil$ を返します.
- inline int Ilog2_s(const int n) : $\log_2 n$ が整数ならその値を返します.整数でないなら -1 を返します.
- inline int Ilog2_s(const ll n) : $\log_2 n$ が整数ならその値を返します.整数でないなら -1 を返します.
- template<class S> S InnerProd(int n, S a[]) : a[0] + a[1] + ... + a[n-1] を返す.
- template<class S, class T> S InnerProd(int n, S a[], T b[]) : a[0]*b[0] + a[1]*b[1] + ... + a[n-1]*b[n-1] を返す.標準的な内積.
- template<class S, class T, class U> S InnerProd(int n, S a[], T b[], U c[]) : a[0]*b[0]*c[0] + ... + a[n-1]*b[n-1]*c[n-1] を返す.
- template<class S, class T, class U, class V> S InnerProd(int n, S a[], T b[], U c[], V d[]) : a[0]*b[0]*c[0]*d[0] + ... + a[n-1]*b[n-1]*c[n-1]*d[n-1] を返す.
- ll crossProd(ll x1, ll y1, ll x2, ll y2) : x1 * y2 - x2 * y1 を返す.平行四辺形の符号付き面積.外積(クロス積)の符号付き長さ.
- int LineIntersection_size(ll x1, ll y1, ll x2, ll y2, ll x3, ll y3, ll x4, ll y4, int p1 = 1, int p2 = 1, int p3 = 1, int p4 = 1) : (x1,y1), (x2,y2) を通る 線1 と (x3,y3), (x4,y4) を通る 線2 が交わるかどうかを返す.交わらないならば0,交わるならば1,無限個の共有点を持つなら2を返す.現状,半直線の場合は未対応(今は例えばp1=2ならp2=2でなければならない).(x1,y1) = (x2,y2) の場合なども未対応.pk = 0 ならば (xk, yk) は端点を含まない,pk = 1 ならば (xk, yk) は端点を含む,pk = 2 ならば線は (xk, yk) 方向には無限に伸びていることを意味する.例えば p1 = p2 = 2 ならば 線1 は両無限に続く直線. p1 = p2 = 1 ならば 線1 は端点を含む線分.きちんとverifyしてない.かなり怪しい.
- template<class T> T TSP_cycle(int n, T **dist, void *mem = wmem) : ノード数nのグラフで全ての頂点をちょうど1回ずつ通る閉路の最短路を求めます.ただし,ノードiからノードjに移動する距離はdist[i][j]です.時間O(n^2 2^n),空間O(n 2^n) のDP.AtCoder Beginner Contest 180 E問題 - Traveling Salesman among Aerial Cities も参考に.
- template<class T> inline T knightDistance(T x, T y) : 無限に広いチェス盤で (0,0) から (x,y) にナイトの駒が移動するのに必要な最小移動回数を返します.
- ll floor_sum(ll n, ll m, ll a, ll b) : $\displaystyle \sum_{k=0}^{n-1} \left\lfloor \frac{ak+b}{m} \right\rfloor$ を返します.AtCoder Libraryの関数をほぼ同じですが,mが0でなければ a, b, m が負などでも動きます.ただし,m が小さい,n, a, b が大きいとオーバーフローするので注意.O(log m) 時間程度.
- ll floor_sum2(ll a, ll k) : $\displaystyle \sum_{i=1}^{k} \left\lfloor \frac{a}{i} \right\rfloor$ を返します.O(min(sqrt(a), k)) 時間程度.
- ll floor_sum2(ll a) : $\displaystyle \sum_{i=1}^{a} \left\lfloor \frac{a}{i} \right\rfloor = \sum_{i=1}^{\infty} \left\lfloor \frac{a}{i} \right\rfloor$ を返します.O(sqrt(a)) 時間程度.
更新履歴
- 20201115-2:
【機能追加っぽいの】
HLD_segtree に枝に重みが載っていると思った場合の各種関数を追加. - 20201115-1:
【機能追加っぽいの】
wgraph構造体に getDistT() を追加.
出力時に配列を展開しない Wt(), Writer() 系を追加.
引数1つの floor_sum2(ll a) を追加.
切り上げ割り算 /+ を /+= の形でも利用可能にした.
三分探索っぽいもの tsearch_min[][]() 系を追加.
【バグ修正っぽいの】
minCostFlow で flim=-1 以外に設定した時の挙動がおかしかったのを修正.
0x1, 0X1, 0b1, 0B1 などを 0*x1 など掛け算に展開しないようにした. - 20201102-1:
【機能追加っぽいの】
rollbackUnionFind 構造体を追加. - 20201101-1:
【機能追加っぽいの】
Mex() を追加.
twoMultisets に getMin(), getMax() を追加.
walloc1d(), walloc2d() に範囲を指定してメモリを確保するのを追加.
【その他っぽいの】
高速ゼータ変換関連をちょっと高速化. - 20201031-1:
【機能追加っぽいの】
AhoCorasick, AhoCorasick_Sum で addWord したときTrie木の終点のノード番号を返すようにした.
twoMultisets 構造体を追加.
【バグ修正っぽいの】
Polynomial構造体で確保メモリの更新を忘れてるバグを修正.
AhoCorasick構造体でメモリの確保する量を間違えていたバグを修正. - 20201026-1:
【機能追加っぽいの】
min[](), max[]() 系の機能を書き直して,範囲の指定方法を拡張して,sum, mul, gcd, lcm を追加.
LHeap の malloc, walloc にメモリ確保と同時に init を走らせるものを追加.
segtree_Point_Maxval を追加.
【バグ修正っぽいの】
remove_reference<>, numeric_limits<> に関して <, > を不等号と認識しないようにした(上のmin[]()系の機能中で使うに困ったため). - 20201018-2:
【機能追加っぽいの】
TSP_cycle() を追加.
sortV(), rsortV() 系列を追加. - 20201018-1:
【機能追加っぽいの】
汎用的なsegtree(segtree_pg, segtree_rg)を作り直したもの segtree_ph 構造体,segtree_rh 構造体を追加(Thanks tailsさん).
【バグ修正っぽいの】
等式,不等式の条件を続けるときに && を省略できます機能で,| や & も || や && と同様な扱いにした(Thanks tailsさん).if(a < b | c < d) はそのままで if(a < b | c && b | c < d) にならなくなった.(ポインタ関係の & を使うとまずいことがあるかも…) - 20201003-1:
【機能追加っぽいの】
rd_string() を追加.
Comb 構造体に dfac, pw2, pw3, pw10, repunit を追加.
【バグ修正っぽいの】
rd_ll() が使えなかったのを修正.
rd(x ? y : z), wt(x ? "hoge" : "piyo) など三項演算子が絡むときに型の推定が x の型になって入出力できなかったのを修正(とりあえず型不明→全型の関数を挿入するように).
【ドキュメントに追記したもの(元々使えるもの)】
wt() で string型の出力について書いてなかったので追記.
これ以前の更新履歴はこちら.
Current time: 2023年03月30日02時05分32秒
Last modified: 2020年11月15日15時58分44秒 (by laycrs)
Tags: no_tags
トップページに戻る
Logged in as: unknown user (not login)