cLay ドキュメント(version 20241019-1)
目次(全然未完成,整理中です…すみません)
概要など
型と変数宣言
入出力
ループ
演算子
グラフ
データ構造
整数
幾何
行列
配列
文字列
典型手法
典型問題
その他(未整理)
更新履歴
概要など
概要
C++っぽい言語をC++言語に変換します.
無駄にライブラリを張るのがめんどくさい,鬱陶しい,ライブラリを整備していても綺麗に書けない処理があるなどを解消したいというのが目的.目的の達成はそのうち….
blogにコード載せる時にライブラリとかincludeとかが長ったらしい,のをなんとかしたいというのも動機の1つです.
C++っぽいコードを標準入力から食わせるとC++のコードが標準出力から出てきます.
#include<bits/stdc++.h>およびgetchar_unlocked,putchar_unlockedなどを使用しているので,それが動かない環境だと動かないし,動かないコードが出てきます.
(その場合は,フラグの欄を見て下さい)
cLayの概要のページはこちら.
最新のバージョンへのリンク:コードとドキュメントを含むzipファイル/プログラムのコード/ドキュメント.
過去のバージョンを含むコードの公開ページはこちら(不定期更新).
ローカルでこのドキュメントを見る場合は,このhtmlファイルと同じフォルダにfontフォルダを作成し,その中にM+ FONTS(mplus-1p-light.ttfなどのmplus-*.ttfファイル)を入れるとオンライン版と同じように表示されると思います.
実際に使用しているのは mplus-1p-light, mplus-1p-heavy, mplus-1p-bold, mplus-1p-regular, mplus-1m-light の5種類だと思います.
未対応なもの
重要度が高い順に,下線付き太字,太字,普通です.重要度が低いものはそもそも対応させる気がないものが多いです.
この欄ではC++ではできるけど…というもの主にリストアップ.
- 共用体・enum
- typedef,defineなどで型を定義するとやばい.他にも括弧使わないと表せない複雑な型(int (*hoge)[10];とか)はダメ
- キャストとかもやばいかも
- 関数を含む式の計算結果の型の推定がうまくいかないはず
- 最近のC言語/C++の仕様
- invalidなコードを食わせると多分落ちる
- validだけど不自然な書き方とか,他にも色々.書ききれない
制限
以下の単語は変数名などで使うとやばい.
基本的にバージョンアップするときは互換性を保ちたいけど,此処の欄は普通に増やす.
色々書いてたけどめんどくさくなったので省略.
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; が最後に補われます.
main関数がなく,if文やfor文などがつかないブロックもない場合,プログラムの最初に int main(){ が,最後に } が補われます.
変数宣言や三項演算子 ? : を使っていない部分での,は(適切に{}を補った上で);に置き換えられます.
その結果,for(;;) i++, break; などと書くと for(;;){ i++; break; } に展開されます.
cpp_int が出てくると,boostの多倍長整数を利用できるように
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
が挿入されます.
フラグ
途中で(プログラムの先頭が推奨)
//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() は使用できなくなります.
- //working_memory=500m : ワーキングメモリのサイズを変更します(詳細はワーキングメモリのところを見てください)
- //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> に,VVI/VII は vector<vector<int>> に,VVVI/VIII は vector<vector<vector<int>>> に置き換えられます.
VLL は vector<long long> に,VVLL は vector<vector<long long>> に,VVVLL は vector<vector<vector<long long>>> に置き換えられます.
VS は vector<string> に置き換えられます.
modint型など
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 に設定されるはずです.
配列の変数宣言
配列のサイズを省略し,初期化もしていない場合,直前に確保した同じ次元の配列の対応する次元サイズになります.
int A[1d5], B[], m[100][100], C[];
int x[][], y[] = {1,2};
int s[10][], t[][];
は以下のようになります.
int A[100000];
int B[100000];
int m[100][100];
int C[100000];
int x[100][100];
int y[] = {1,2};
int s[10][100];
int t[10][100];
配列の初期化は,現状 = を明示的に書いている場合のみ初期化しているとみなします.
以下の例で,int y[]{1,2} と書いた場合は,int y[1d5]{1,2} と 1d5 が配列サイズとして設定されます.
将来的に変更される可能性があります.一般的な用途としては,一応この場合は配列サイズを省略しないのを推奨とさせてください.
入出力
入力
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型と,vector, set, multiset, pairの組合せのみに対応.他の型もそのうち対応.
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]);
}
vector, set, multiset, pair については,中身の要素をスペース区切りで1行にまとめたものがひとかたまりとみなされます.
中身が整数であっても [B=2] などで進数を指定しないでください.現状は指定しても10進数で表示し,将来的に仕様は変わる可能性があります.
例えば,
int a = 1;
VI b{2,3,4};
int c = 5;
wt(a,b,c);
wt("---");
wtLn(a,b,c);
wt("---");
wtLn(a,b(b.size()),c);
に対する出力は
1 2 3 4 5
---
1
2 3 4
5
---
1
2
3
4
5
となり,
set<VS> s;
s.insert(VS{"hoge", "piyo", "hoge"});
s.insert(VS{"a", "z"});
s.insert(VS{"hoge", "piyo", "hoge"});
wtLn(s);
に対する出力は
a z hoge piyo hoge
となり,
multiset<VS> s;
s.insert(VS{"hoge", "piyo", "hoge"});
s.insert(VS{"a", "z"});
s.insert(VS{"hoge", "piyo", "hoge"});
wtLn(s);
に対する出力は
a z hoge piyo hoge hoge piyo hoge
となり,
pair<int,pair<double,string>> p = make_pair(10, make_pair(20.0, "abc"));
wt(p);
に対する出力は
10 20.000000000000000 abc
となります.
細かい注意
reader(), writer() は複数の文に分割されることがあります.
1文に reader(), writer() が単独でなくなる場合に現状うまく動きません.
(とりあえずは ; で区切って単独の文にしてもらえると助かります)
wt(1?1:0),wt(1); // うまく動かない
if(cond) a=a?a:b, wt(a); // うまく動かない
wt(1),wt(2); // 三項演算子がないので wt(1); wt(2); となりwt単独になるので問題なく動く
ファイル入出力
標準入出力ではない場合は,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に書き込み
という感じです.
変数宣言時の入力
変数を宣言した際に変数名の前に@をつけると,その時点でrd()を利用して入力します.
@と変数名の間,または変数名の後に++や--を書くと,読み込んだ瞬間インクリメント,デクリメントをします.
(int @++A; でも int @A++; でも int @++A[10]; でも int @A++[10]; でも int @A[10]++; でも良いですが,int ++@A; や int @A[++10]; はやめて下さい.通常の意味の int @A[N++]; も避けて下さい)
配列の場合は,配列サイズだけ読み込みます(2次元以上の配列でも大丈夫です).
{
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);
}
となります.
{
int @N, @A++[N];
wt(mul(A(N)));
}
は
{
int N;
scanf("%d",&N);
int A[N];
int i;
for(i=0;i<N;i++) scanf("%d",&A[i]);
for(i=0;i<N;i++) A[i]++;
int res = 1;
for(i=0;i<N;i++) res *= A[i];
printf("%d\n", res);
}
の意味です.
int @(A,B)[N]; で A[0], B[0], A[1], B[1], ... の順番で読み込みます.
(この書き方は @ が必須です.int (A,B)[N]; で入力を読み込まずに変数宣言だけ行うことはできません)
また,変数名の前に,型を書くと,その変数だけその型になります.
int @N, @(A,B++)[N];
は
int N;
scanf("%d",&N);
int A[N], B[N];
for(int i = 0; i < N; i++){
scanf("%d%d\n",A+i,B+i);
B[i]++;
}
の意味となり,
int @N, @(A,B,ll C,D)[N];
は
int N;
scanf("%d",&N);
int A[N], B[N]; ll C[N]; int D[N];
for(int i = 0; i < N; i++) scanf("%d%d%lld%d\n",A+i,B+i,C+i,D+i);
の意味になります.
また,vector に関しては,@変数名(要素数) という形で宣言した場合に,標準入力より読み込みます.
例えば,
int @N;
VLL @(A,B)(N);
VI @C(N);
は
int N;
scanf("%d",&N);
vector<long long> A(N), B(N);
for(int i = 0; i < N; i++) scanf("%lld%lld\n",&A[i],&B[i]);
vector<int> C(N);
for(int i = 0; i < N; i++) scanf("%d\n",&C[i]);
の意味になります.
チェック付き入力
- ll cReader_ll(ll mn, ll mx, char nx) : ll を読み込みます.
読み込んだ値がmn以上mx以下でない場合は assert() により落ちてREになります.
また,値の前に余分なスペースや文字などがある場合も assert() により落ちてREになります.
読み込んだ直後の文字が nx でない場合も assert() により落ちてREになります(nxまで読み込んだことになり,次はnxの次の文字から読み込みます).
先頭に余分な0がある場合(0038, -01,00 など),「-0」と0の前にマイナス記号が存在する場合も落ちます.
- void cReader_eof() : これ以上入力が続かないことを確認します.次の文字を確認してEOFでなければ assert() により落ちてREになります.
入力形式が
N
A B C
で制約が $1 \leq N \leq 100$,$0 \leq A \leq B \leq C \leq 10^9$ の場合は
ll N, A, B, C;
N = cReader_ll(1, 100, '\n');
A = cReader_ll(0, 1d9, ' ');
B = cReader_ll(A, 1d9, ' ');
C = cReader_ll(B, 1d9, '\n');
cReader_eof();
などで入力形式等が正しいかチェックしながら読み込むことができます.
ループ
演算子
グラフ
データ構造
UnionFind (UF, Disjoint Set Union, USU)
unionFind構造体.unionFind uf; で宣言したとして以下の通り.
- unionFind() : コンストラクタ.何もしない.
- unionFind(const char mode, const int n, void **mem = &wmem) : コンストラクタ.mode='m'の場合は以下の malloc(n) を,mode='w'の場合は以下の walloc(n, mem) を呼び出します.
- unionFind(const char mode, const int n, const int fg, void **mem = &wmem) : コンストラクタ.mode='m'の場合は以下の malloc(n, fg) を,mode='w'の場合は以下の walloc(n, fg, mem) を呼び出します.
- 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 fg, 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[]): 各グループのサイズからなる配列を返す.返り値はグループ数.
- int uf.comp(int res[], void *mem = wmem): 属すグループの配列 arr[i] = uf(i) を座標圧縮した結果の配列 res を求めます.グループ数の数 = max(res)+1 を返します.
weightedUnionFind 構造体.いわゆる重み付きUnionFind.
weightedUnionFind<T> uf; で宣言したとして以下の通り.
unionFindとの差分のみを記述.connectの使い方が違うのと,diffが追加されています.
- int uf.connect(int a, int b, T c): aとbを同じグループにまとめ weight[b]-weight[a]=cと設定します.aとbが既に同じグループなら何も処理せず0を返し,そうでなければ1を返す
- int uf.operator()(int a, int b, T c): connect(a,b,c)と同じ
- T uf.diff(int a, int b): weight[b]-weight[a]を返す.ただし,a,bが同じグループでなければ何を返すか不定です.
weightedUnionFind<ll> uf('w',100,1);
uf(10,70,1000LL);
uf(70,90,2000LL);
if(uf(10)==uf(90)){
wt("connected");
wt(uf.diff(10,90)); // 3000
}
rollbackUnionFind 構造体.rollbackUnionFind uf; で宣言したとして以下の通り.
ただし,unionFindで使えるものは全てそのまま使える.
経路圧縮をしないので,一部の計算量はアッカーマン逆関数からlogになっている.
- void uf.snapshot() : スナップショットを撮る(1回もスナップショットを撮ってなければスナップショットは初期値の状況)
- void uf.rollback() : 最後にスナップショットを撮ったところまで巻き戻す
- int uf.undo() : connectなどで最後に非連結で繋げた枝を消す(connectの戻り値が0のものはカウントしない).スナップショットを撮ったより過去に戻った場合はスナップショットは壊れるかもしれない(現時点では,戻ったときの状態が新しくスナップショットになる).
整数
桁・n進数
配列の最初の要素が一番小さい桁に対応します(i番目の要素が10^iに対応,など).
最後に_rがついている関数に関しては,逆に配列の最初の要素が一番大きい桁に対応します(数値を文字列として読み込んだ場合に対応,など).
- 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> inline T invDigit_r(int sz, T d[]) : sz桁の10進数d[]の値を返します.sz=4, d[]={4,6,1,0}のとき4610を返す.
- 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> inline T prodDigits(T n) : nを10進数で書いたときの各桁の積を返す.n=0のときは0を返す.
- template<class T, class S> inline int DigitHist(T n, S res[]) : 正の整数nを10進数で書いたときの桁数を返し,「res[i] = 数字iが登場する回数(i=0,1,...,9)」を代入する.
- int isPalindromicNumber(int n) : nが回文数なら1を,そうでないなら0を返す.
- int isPalindromicNumber(ll n) : nが回文数なら1を,そうでないなら0を返す.
- __int128_t kthPalindromicNumber64(ll k) : k番目の回文数を返す.0番目は0,1番目は1,7番目は7,10番目は11,11番目は22.$O(\log k)$ 時間程度.
- ll reverseNumber(ll n) : 桁をひっくり返した数を返す.613なら316を,780なら87を返す.
- inline int STR2int(string s, const int b) : s が表す文字列をb進数の値と見て,その値を int 型で返す.文字は0..9A..Za..zの順で62進数まで.マイナスもOKだけど普通の書き方以外は対応してない.
- inline int STR2int(string s) : s が表す文字列を10進数の値と見て,その値を int 型で返す.
- inline ll STR2ll(string s, const int b) : s が表す文字列をb進数の値と見て,その値を ll 型で返す.文字は0..9A..Za..zの順で62進数まで.
- inline ll STR2ll(string s) : s が表す文字列を10進数の値と見て,その値を ll 型で返す.
ルートとk乗根
どれもllの最大値に近いとうまく動かない可能性がある(4*10^18ぐらいまでを目安に).
整数の平方根.(ただし 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 と同じ.
整数のk乗根.
現在は非負整数のみに対応だが,将来的に負の整数に対応する可能性がある.
- inline ll Iroot_f(const ll n, const ll k) : 非負整数 n に対して,$\lfloor \sqrt[k]{n} \rfloor$ の値を返す.
- inline ll Iroot_c(const ll n, const ll k) : 非負整数 n に対して,$\lceil \sqrt[k]{n} \rceil$ の値を返す.
- inline ll Iroot_s(const ll n, const ll k) : 非負整数 n に対して,$x^k = n$ なる非負整数 x の値を返す.ただし,存在しないなら -1 を返す.
- inline ll Iroot(const ll n, const ll k) : Iroot_f と同じ.
log2
整数の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 を返します.
ビット
ビットに関すること.
- BIT_Ith(i) : (1LL<<(i)) に展開されます.
- BIT_Ith(i,j) : (((i)>>(j))&1) に展開されます.j ビット目が立ってれば 1,そうでなければ 0.
- 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)) に展開されます.一番下のビットのみ残ります.i=22=2+4+16 なら BIT_lowest(i) は 2 になります.
- BIT_nonlowest(i) : ((i) & ((i)-1)) に展開されます.一番下のビットのみ消えます.i=22=2+4+16 なら BIT_nonlowest(i) は 20 (4+16) になります.
- 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以外のコンパイラだと多分動かないので注意.
- inline int BIT_parity(const int x) : __builtin_parity(x)
- inline int BIT_parity(const ll x) : __builtin_parityll(x)
- inline int BIT_parity_pm(const int x) : 1 - 2 * __builtin_parity(x).1のビットの個数が偶数個なら1,奇数個なら-1を返します.
- inline int BIT_parity_pm(const ll x) : 1 - 2 * __builtin_parityll(x).
- template<class T> int arr2bit_int(const int N, const T A[], const T base = 0) : (A[i] - base) ビット目が全部立ってる int 型の整数を返します.(1 << (A[0] - base)) | (1 << (A[1] - base)) | ... | (1 << (A[N-1] - base)).
- template<class T> int arr2bit_int(const string S, const char base = 0)
- template<class T> ll arr2bit_ll(const int N, const T A[], const T base = 0)
- template<class T> ll arr2bit_ll(const string S, const char base = 0)
幾何
行列
配列
ソート(配列)
配列に関するソート.
状況に応じてバケツソート・基数ソート・イントロソートなどを使い分けます.
変な不都合が出る場合などは,単純にイントロソート(srd::sortを使う)をする sortI() を使ってください.
現状は,配列の数が3以上になると,イントロソートのみを試します.
- void sortA(int N, S A[], T B[], ..., void *mem = wmem) : 要素数Nの1個~4個の配列を (A[i], B[i], ...) が辞書式順序で小さい順にソートします.ワーキングメモリを使用します.
- void sortA_index(int N, S A[], T B[], ..., void *mem = wmem) : sortAのどの要素がどこに移動したかも求めるバージョン.一番最後に指定した配列に対して z[i]=i を代入してから sortA を行います.配列数(zも含む)は2~4.
- 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 sortI(int N, S A[], T B[], ..., void *mem = wmem) : 要素数Nの1個~4個の配列を (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に負けるかも.大体大丈夫だと思うけどもしかしたら正常に動かない環境があるかも.
ここまで(以下は古いもの・あまり使わないもの)
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)
ソート(変数)
- sortE(a, b, c, ...) : バブルソートで昇順にするコードに展開します.現状 a, b, c, ... は全て同じ型でないと動きません.
- rsortE(a, b, c, ...) : バブルソートで降順にするコードに展開します.現状 a, b, c, ... は全て同じ型でないと動きません.
主に1つの配列に関する処理
スライス
添字が bg から ed まで step 飛ばしで 配列 A の要素を拾ってできる配列を返す求める.
A[bg], A[bg + step], A[bg + 2*step], A[bg + 3*step], ..., A[bg + (len-1)*step] みたいなのを返す.
step が正なら,bg 以上 ed 未満の添字からなる
step が負なら,bg 以下 ed より大きい添字からなる
N を配列Aの長さとして,添字が 0 以上 N 未満でなければ,A は循環していると思って A[ind] = A[ind mod N] みたいに思う
- template<class T, class S> int Slice(const int N, const T A[], const ll bg, const ll ed, const ll step, S res[]) : 結果は配列 res に格納.戻り値は res の長さ
- template<class T> vector<T> Slice(const vector<T> &A, const ll bg, const ll ed, const ll step)
- string Slice(const string &A, const ll bg, const ll ed, const ll step)
int N = 10, A[10] = {0,1,2,3,4,5,6,7,8,9};
int len, B[20];
len = Slice(N,A,0,2N,1,B);
wt(len, ":", B(len));
len = Slice(N,A,10000000000005LL,10000000000010LL,2,B);
wt(len, ":", B(len));
len = Slice(N,A,5,0,-1,B); // including A[5], excluding A[0]
wt(len, ":", B(len));
len = Slice(N,A,3,-40,-13,B); // (A[3], A[-10], A[-23], A[-36]) = (A[3], A[0], A[7], A[3])
wt(len, ":", B(len));
len = Slice(N,A,3,10,-1,B);
wt(len, ":", B(len));<MYFORMAT_TEX_BR>
/*
20 : 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
3 : 5 7 9
5 : 5 4 3 2 1
4 : 3 0 7 4
0 :
*/
配列のシフト
- 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以上だったりマイナスでも大丈夫なはずです.
主に1つの配列に関する計算
相異なる要素数をカウントする
- 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 T> int Distinct(vector<T> A[], int sorted=0, void *mem = wmem)
- 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 Count(const int N, const T A[], const S val) / template<class T, class S> int arrCountVal(const int N, const T A[], const S val) : 要素数 N の配列 A[] 中の val と一致する要素数を返します.
- template<class T, class S> int Count(const vector<T> &A, const S val) / template<class T, class S> int arrCountVal(const vector<T> &A, const S val)
- template<class S> int Count(const string &A, const S val) / template<class S> int arrCountVal(const string &A, const S val)
とある要素が連続する最大長さを求める
- template<class T, class S> int arrCountValSeqMax(const int N, const T A[], const S val) : 要素数 N の配列 A[] 中の val が連続して登場する最大の長さを返します.登場しないなら0を返します.
- template<class S> int arrCountValSeqMax(const string A, const S val)
回文にするのに変えないといけない要素数
A[i] != A[N-1-i] の数を数える(i=0,1,...,N/2-1)
- template<class T> int PalindromeCost(const int N, const T A[])
- template<class T> int PalindromeCost(const vector<T> A)
- int PalindromeCost(const string A)
string str1 = "aabbcc", str2 = "abcbd";
wt(PalindromeCost(str1), PalindromeCost(str2));
// 2 1
string str = "aabbaa";
rep(i,1,str.size()+1) wt(i, ":", PalindromeCost(str.substr(0,i)));
// 1 : 0 (a)
// 2 : 0 (aa)
// 3 : 1 (aab)
// 4 : 2 (aabb)
// 5 : 1 (aabba)
// 6 : 0 (aabbaa)
Mex
- 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})
K番目の要素
- 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を使用.
主に2つの配列に関する処理
ソートしながらマージする(マージソートのマージ)
- template<class T1, class T2, class T3> int arrMerge(int As, T1 A[], int Bs, T2 B[], T3 res[]) : 配列A[], B[]はソート済み.配列AとBを繋げてソート済みにした配列res[]を求める.戻り値はresの配列のサイズAs+Bs.
- template<class T1, class T2, class T3> int arrMergeD(int As, T1 A[], int Bs, T2 B[], T3 res[]) : 配列A[], B[]はソート済み.配列AとBを重複を除きマージしてソートした配列res[]を求める.厳密には要素xについて配列Aにi個,配列Bにj個ある場合,配列resの中にはmin(i,j)個になる.戻り値はresの配列のサイズ.
マージテク(要素数が小さい方だけ移動させることで計算量を削減するテクニック)のマージ
AにBの要素を追加して,Bを空にする(並び順はどうなるか不明)
- template<class T> void MergeTech(set<T> &A, set<T> &B)
- template<class T> void MergeTech(multiset<T> &A, multiset<T> &B)
- template<class T> void MergeTech(vector<T> &A, vector<T> &B)
set<int> a1( {3,4,5,6} );
set<int> b1( {1,2,4,8,16} );
MergeTech(a1,b1);
wt(a1); // 1 2 3 4 5 6 8 16
wt(b1); // none
multiset<int> a2( {3,4,5,6} );
multiset<int> b2( {1,2,4,8,16} );
MergeTech(a2,b2);
wt(a2); // 1 2 3 4 4 5 6 8 16
wt(b2); // none
vector<int> a3( {3,4,5,6} );
vector<int> b3( {1,2,4,8,16} );
MergeTech(a3,b3);
wt(a3); // 1 2 4 8 16 3 4 5 6
wt(b3); // none
主に2つの配列に関する計算
比較
- 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的な感じ).
ワイルドカード文字がある場合の一致判定
- int WildEQ(const string &A, const string &B, const char wc) : 文字wcは自由な1文字に変更可能として,文字列AとBが一致させることができるならば 1,そうでないなら 0 を返します
string a = "at**der";
string b = "a**oder";
string c = "c******";
string d = "******";
wt( WildEQ(a,b,'*') ); // 1
wt( WildEQ(a,c,'*') ); // 0
wt( WildEQ(a,d,'*') ); // 0
配列Aの 最初, 最後 の部分が配列Bに一致するかを調べる(関数名に三単現のsはないです)
- template<class T1, class T2> int startWith(const int As, const T1 A[], const int Bs, const T2 B[]) : $As \geq Bs$ かつ A[i] = B[i] (i=0,1,..,Bs-1) が成り立つなら 1,そうでないなら 0 を返す
- template<class T1, class T2> int startWith(const vector<T1> &A, const vector<T2> &B)
- int startWith(const string &A, const string &B)
- template<class T1, class T2> int endWith(const int As, const T1 A[], const int Bs, const T2 B[]) : $As \geq Bs$ かつ A[As-1-i] = B[Bs-1-i] (i=0,1,..,Bs-1) が成り立つなら 1,そうでないなら 0 を返す
- template<class T1, class T2> int endWith(const vector<T1> &A, const vector<T2> &B)
- int endWith(const string &A, const string &B)
string a = "abc";
string b = "ab";
string c = "bc";
wt( startWith(a,b) ); // 1
wt( startWith(a,c) ); // 0
wt( startWith(b,a) ); // 0
wt( endWith(a,b) ); // 0
wt( endWith(a,c) ); // 1
wt( endWith(c,a) ); // 0
主に複数の配列に関する処理
- template<class S> S 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 に変更される.削除した要素を返す(削除する要素数が1の場合のみ).
- 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> 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, class S> inline int vec2arr(vector<T> &v, S arr[]) : vectorを配列に変換します.arr[i] = v[i] して要素数を返します.
- template<class T, class S1, class S2> inline int vec2arr(vector<vector<T>> &v, S1 arr2[], S2 arr2[]) : vectorを配列に変換します.arr1[i] = v[i][0], arr2[i] = v[i][1] して要素数を返します.
- template<class T, class S1, class S2, class S3> inline int vec2arr(vector<vector<T>> &v, S1 arr2[], S2 arr2[], S3 arr3[]) : vectorを配列に変換します.arr1[i] = v[i][0], arr2[i] = v[i][1], arr3[i] = v[i][2] して要素数を返します.
主に複数の配列に関する計算
典型手法
尺取法(TwoPointers)
長さ n 配列の配列 a に対して,条件が成り立つ区間(空でない連続部分列)を尺取法で求めるものです.
ただし,条件は「空の区間に対して成り立つ」と「条件が成り立つ区間に包含される区間でも成り立つ(短いほど成り立ちやすい)」が成り立たなければいけません.
(実際には,a は配列である必要はありませんが,配列として説明しています)
初め空の区間から初めて,要素を追加したり,削除しながら条件が成り立つか調べます.
TwoPointers(n, res, ind)[追加][削除][条件]; という形式で記述します.
追加では,a[ind](ind番目の要素)が追加されるときの処理,
削除では,a[ind](ind番目の要素)が削除されるときの処理,
条件では,条件を満たしているかどうか調べる関数の中身(return で bool を返す)を記述してください.
この記述の前に必要な前処理(初期化)をしておいてください.
resは計算結果を格納する整数の配列(長さn以上)で,j=res[i] というのは,区間 [i,i), [i,i+1), [i,j) は条件を満たすが [i,j] は条件を満たさない(または j=n)を意味します.
条件を満たす区間の最大長は max(res[i] - i) で,区間の数は sum(res[i] - i) です.
例えば,非負整数からなる配列 a に対して,和が10以下になる区間を列挙するには以下のように記述します.
int a[10] = {3,5,1,4,2,11,4,6,1,2};
int res[10], s = 0; // 和 s は 0 で初期化しておく
TwoPointers(10, res, ind)[
s += a[ind];
][
s -= a[ind];
][
return s <= 10;
];
rep(i,10) wt(i,res[i]);
wt("max_len", max[i,0,10](res[i]-i));
wt("num", sum[i,0,10](res[i]-i));
// 0 3
// 1 4
// 2 5
// 3 5
// 4 5
// 5 5
// 6 8
// 7 10
// 8 10
// 9 10
// max_len 3
// num 20
そして,これはだいたい以下のような感じで展開されます(機能追加時の実装っぽいものです).
{
int a[10] = {3,5,1,4,2,11,4,6,1,2};
int res[10], s = 0;
auto TP_insert = [&](int ind){
s += a[ind];
};
auto TP_erase = [&](int ind){
s -= a[ind];
};
auto TP_check = [&](void){
return s <= 10;
};
{
int i = 0, j = 0, n = 10;
for(;;){
if(TP_check()){
res[i] = j;
if(j == n) break;
TP_insert(j++);
} else {
TP_erase(i++);
res[i] = res[i-1];
}
}
while(i+1 < n) res[++i] = j;
}
rep(i,10) wt(i,res[i]);
wt("max_len", max[i,0,10](res[i]-i));
wt("num", sum[i,0,10](res[i]-i));
}
典型問題
その他(未整理)
ループ
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個以上ドットが並ぶとバグる実装なのでそのうちなんとかする.
特殊なループ1(多重ループ)
- 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] < m$ かつ $d[i] \neq d[j]$.多分低速なので注意.
- rep_marr(d,n,m) : 0 から m-1 までの整数から順序を加味して重複を許して n 個取り出す組合せのループ.$0 \leq d[0], d[1], \ldots, d[n-1] < m$.
たとえば,
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 の説明を参照.
特殊なループ2(主に2次元以上で一定以内の距離を走査するループ)
例から書くと
rep_dist(i, j, 3, 8) wtF("({i},{j})");
// (3,8)からマンハッタン距離で1以下の距離にある((3,8)は除く)
// (2,8)(3,7)(3,9)(4,8)
rep_dist(i, j, k, 3, 8, 1) wtF("({i},{j},{k})");
// (3,8,1)からマンハッタン距離で1以下の距離にある((3,8,1)は除く)
// (2,8,1)(3,7,1)(3,8,0)(3,8,2)(3,9,1)(4,8,1)
rep_dist(i, j, 3, 8, m2z) wtF("({i},{j})");
// (3,8)からマンハッタン距離で2以下の距離にある((3,8)自身も含む)
// (1,8)(2,7)(2,8)(2,9)(3,6)(3,7)(3,8)(3,9)(3,10)(4,7)(4,8)(4,9)(5,8)
rep_dist(i, j, 3, 8, t2) wtF("({i},{j})");
// (3,8)からチェビシェフ距離で2以下の距離にある((3,8)は除く)
// (1,6)(1,7)(1,8)(1,9)(1,10)(2,6)(2,7)(2,8)(2,9)(2,10)(3,6)(3,7)(3,9)(3,10)(4,6)(4,7)(4,8)(4,9)(4,10)(5,6)(5,7)(5,8)(5,9)(5,10)
rep_dist(i, j, 3, 8, e3) wtF("({i},{j})");
// (3,8)からユークリッド距離で3以下の距離にある((3,8)は除く)
// (0,8)(1,6)(1,7)(1,8)(1,9)(1,10)(2,6)(2,7)(2,8)(2,9)(2,10)(3,5)(3,6)(3,7)(3,9)(3,10)(3,11)(4,6)(4,7)(4,8)(4,9)(4,10)(5,6)(5,7)(5,8)(5,9)(5,10)(6,8)
rep_dist(i, j, 3, 8, ee2) wtF("({i},{j})");
// (3,8)からユークリッド距離でsqrt(2)以下の距離にある((3,8)は除く)
// (2,7)(2,8)(2,9)(3,7)(3,9)(4,7)(4,8)(4,9)
一般的には
- rep_dist(x1,x2,...,xk, c1,c2,...,ck, mode) : modeは省略可能
という形式です.
x1,...,xk がループ変数(定義されてなければint型で勝手に定義します)で,c1,...,ckが中心の座標を表します.
modeは以下を連結したものを指定します:
- 距離の種類「m:マンハッタン距離」「tまたはc:チェビシェフ距離」「e:ユークリッド距離」「ee:ユークリッド距離の2乗」,省略するとmになります.
- これ以下の距離を操作するという整数の値,省略すると1になります
- 中心自身もループに含める場合はzを指定します
距離の定義は以下の通りです:
- マンハッタン距離の定義は $\sum |x_i - c_i|$.
- チェビシェフ距離の定義は $\max |x_i - c_i|$.
- ユークリッド距離の定義は $\sqrt{\sum (x_i - c_i)^2}$.
距離を指定する場合,あまり大きな整数を指定することは考えてません(バグったり不具合が起こる可能性が高いです).
ループの順番は辞書順で回るはずです.
演算子
昔のGCC拡張で存在した <? および >? 演算子の同時に代入するバージョンのみ使えます.
つまり,a <?= b; は a = min(a, b); と,a >?= b; は a = max(a, b); と大体同じです.
代入しないバージョンは素直にminおよびmaxを使うこと.
a >?= b > 10 は a >?= (b > 10) とみなされます.必要なら (a >?= b) > 10 などと括弧を付けてください.
(式 a >?= b の評価値は代入後の a の値,つまり max(a,b) です)
正の整数に関する繰り上げの割り算演算子 /+ が使えます.
13 /+ 3 は 5 となります.
実際には a /+ b は (a+b-1) / b で定義され,正の整数でないと繰り上げ割り算になる保証はありません.
また,a+b-1 がオーバーフローする可能性があるので要注意です.
a = a /+ b; は a /+= b; と書くこともできます.
a /+= b > 10 は a /+= (b > 10) とみなされます.必要なら (a /+= b) > 10 などと括弧を付けてください.
負などもありうる場合には以下の関数が使えます.
- 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; と書くこともできます.
a %%= b > 10 は a %%= (b > 10) とみなされます.必要なら (a %%= b) > 10 などと括弧を付けてください.
べき乗演算子 ** が使えます.
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; と書くこともできます.
a **= b > 10 は a **= (b > 10) とみなされます.必要なら (a **= b) > 10 などと括弧を付けてください.
数値の後の掛け算の演算子*は省略できることがあります.
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 < hoge > hoge < hoge > hoge などの場合は,テンプレート絡みなどで使用することがあるので,展開されません(左から右に単調非減少の形で条件を書くと良いと考えています).
複数の演算(内部でarraylike operationと呼んでるもの)
(a1,a2,...,an) += (b1,b2,...,bn); のような形で複数の演算を一度に行います.
コンマ演算子をオーバーロードしていると変なことが起こるかもしれないので,この機能を無効化する場合は //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;
}
と展開されます.
また,(a,b,c)++; や --(a,b); はそれぞれ a++; b++; c++; や --a; --b; に展開されます.
更に,現状では
(a, b, c) *= (f(m, n) + k * (b, c, a) + (s, t)) % 100;
は
{
auto tmp1 = (f(m, n) + k * b + s) % 100; // 実際は s とかは (s) とカッコが付いたりします
auto tmp2 = (f(m, n) + k * c + t) % 100;
auto tmp3 = (f(m, n) + k * a + s) % 100;
a *= tmp1;
b *= tmp2;
c *= tmp3;
}
に展開されます.
共通部分は (hoge, piyo) の形式で書かなければ良いです.
また,サイズが違う場合はサイクリックに適応されます(R言語のような感じ).
現状では,f(n)が3回呼び出されますが,将来的に変更される可能性があります(右辺に副作用があるものを書かないでください).
複数の処理(内部でarraylike sentenceと呼んでるもの)
arraylike operationのある種の一般化です.
@[a,b] と@[]内に,で区切ったものが展開されます.
unionFind u_a, u_b, u_c;
u_@[a,b,c].walloc(10,1);
で
unionFind u_a, u_b, u_c;
u_a.walloc(10,1);
u_b.walloc(10,1);
u_c.walloc(10,1);
に展開されます.
また,
unionFind u_a, u_b, u_c;
u_@[a,b,c].walloc(@[10,20,30],1);
で
u_a.walloc(10,1);
u_b.walloc(20,1);
u_c.walloc(30,1);
に展開されます.
中身の要素の長さが違う場合は,短いものはR言語っぽくサイクリックに拡張されます.
また,2種類以上,独立に展開する場合は,@1[a,b],@2[a,b]と@+数字1文字で11種類使えます.
u_@1[a,b,c].walloc(@2[10,20,30],1);
は
u_a.walloc(10,1);
u_b.walloc(10,1);
u_c.walloc(10,1);
u_a.walloc(20,1);
u_b.walloc(20,1);
u_c.walloc(20,1);
u_a.walloc(30,1);
u_b.walloc(30,1);
u_c.walloc(30,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$ 以下でないと正しく動きません.
関数のメモ化
関数の引数に続いて : で区切ってMemoizeをつけると,同じ引数で呼び出されたとき,以前に計算した結果を即座に返すようにします.
引数が配列(ポインタ)とか変なのを含む場合は動かないと思います.
Modint f(int n : Memoize){
if(n<=1) return n;
return f(n-1) + f(n-2);
}
でフィボナッチ数を計算する関数をメモ化します.
標準では map を使用してメモするため,f(n) を呼び出したとき,この場合は,O(n log n) 時間,O(n) 領域使うと思います.
f(n) を計算するときに,直接的,または,間接的に f(n) を呼び出すと動きません(多分無限ループ).
配列にメモする場合は,
Modint f(int n : Memoize[1d5]){
if(n<=1) return n;
return f(n-1) + f(n-2);
}
とすると,n が 0 から 99999 (=10^5-1) しか呼び出さないことを仮定して Modint memo[1d5]; を用意してメモ化します.
0から始まらない場合
Modint f(int n : Memoize[-3:123]){
if(n<=1) return n;
return f(n-1) + f(n-2);
}
で n が -3 以上 123 以下と仮定して Modint memo[127]; を使ってメモ化します(127 = 123 - (-3) + 1).
2次元以上の場合は,範囲を,で区切って書きます.
Modint f(int x, int y : Memoize[-1:10,15]){
hoge
}
は x が -1 から 10 まで,y は 0 から 14 までの範囲と仮定してメモ化します.
計算結果を消去したい場合は f_clear() を呼び出して下さい(現状全部消えます,配列の場合,サイズに比例した時間がかかります).
fは元々の関数名で,対応するものに変更してください.
ワーキングメモリ
ワーキングメモリを必要な関数や機能などを使うと勝手にワーキングメモリを確保します.
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番目の要素の和を返します.-2番目,-1番目,N番目,N+1番目などは0とみなします.bよりaのほうが大きい場合は0を返します.$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
}
演算を + ではなく ^ にしたバージョンは fenwick_xor<T> です.
基本的な機能は同じですが,kth() がありません.
バイナリヒープ(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)$.
以下は LHeap<T> h; で定義したとする.
よく使いそうなもの:
- void h.malloc(int N):$N$ 要素分のメモリを確保
- void h.malloc(int N, int ini):$N$ 要素分のメモリを確保し,iniが0でなければ init(N) もやる
- void h.walloc(int N, void **mem=&wmem):$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返す
- void h.walloc(int N, int ini, void **mem=&wmem):$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返し,iniが0でなければ init(N) もやる
- void h.free(void):h.malloc(int N)で確保したメモリの解放
- void h.init(int N):初期化.今後 $N$ 要素の配列を使う.ヒープの中は空.
- void h.change(int n, T v):$A_k = v$ に変更する.ヒープに入っていない要素ならヒープに突っ込み,入っている要素だったら,適切な位置に移動する.
- int h.pop(void):ヒープに入っている最小の要素のインデックスを返し,その要素を消す.最小が $A_k$ なら $k$ を返す.
- int h.size:ヒープに入っている要素の数.
- int h.hp[]:$\verb|hp[k]|$ でヒープに入っている $k$ 番目の要素のインデックス.$\verb|pop()|$ で取り出される要素のインデックスは $\verb|hp[0]|$.
- T h.val[]:$A_k$ の値 $\verb|val[k]|$.
そうではないもの:
- int h.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|malloc(int N, int init_fg)|$:malloc(N) する.その後,init_fg は 0 でないなら init(N) もする
- $\verb|walloc(int N, void **mem = &wmem)|$:$\verb|mem|$ から $N$ 要素分のメモリを確保して,次のアドレスを返す
- $\verb|walloc(int N, int init_fg, void **mem = &wmem)|$:walloc(N,mem) する.その後,init_fg は 0 でないなら init(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.getAt(int i) : At : i番目の要素を返します
- 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番目の要素の積を返します
- T t.getOr(int a, int b) : Or : a番目の要素,a+1番目の要素,…,b-1番目の要素のビットごとのorを返します
- T t.getAnd(int a, int b) : And : a番目の要素,a+1番目の要素,…,b-1番目の要素のビットごとのandを返します
- T t.getXor(int a, int b) : Xor : a番目の要素,a+1番目の要素,…,b-1番目の要素のビットごとのxorを返します
- 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_Or<T> t
- segtree_Point_And<T> t
- segtree_Point_Xor<T> t
- segtree_Point_Minval2<T> t
- segtree_Add_Minval<T> t
- segtree_Change_Sum<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 で,
int 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 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の場合は何を返すか不定です.
- template<bool (*f)(SVAL)>int t.max_right(int a, int mx) : fはtemplateで指定された関数として f(g(v[a], v[a+1], ..., v[b-1])) が true になる最大の b を返します(単調性を仮定します).ただし b の値は mx まで調べます(f(h(...))の...の部分にv[mx以上]が含まれていたら即座にfalseになります).
- template<bool (*f)(SVAL)>int t.max_right(int a) : min_left(a, trueN) です.ただし trueN は setN で設定した時の引数の N です.
- template<bool (*f)(SVAL)>int t.min_left(int b, int mn) : fはtemplateで指定された関数として f(g(v[a], v[a+1], ..., v[b-1])) が true になる最小の a を返します(単調性を仮定します).ただし a の値は mn まで調べます(f(h(...))の...の部分にv[mn未満]が含まれていたら即座にfalseになります).
- template<bool (*f)(SVAL)>int t.min_left(int b) : min_left(b, 0) です
AtCoder Libraryのlazy_segtreeとだいたい同じ機能です.実装方法は例えば以下を参考にしてください.
AtCoder Library Practice Contest K問題 - Range Affine Range Sum
AtCoder Library Practice Contest L問題 - Lazy Segment Tree
Codeforces Round #684 DIV1 C問題 - Greedy Shopping (二分探索の参考)
また,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 はグローバルに関数を定義する形式から名前をつけています.
static segment tree
segment treeではないです(segment treeと同じインターフェースで利用できるようにしています).
範囲に足し算するのは差分を2箇所操作する,和を求めるには累積和を事前計算しておくなどして,
変更クエリ・取得クエリのみを続けて行う際は,最初の1回除きできるだけ効率よく計算できるようにします.
変更クエリと取得クエリを繰り返して行うと性能が劣化します.
セグメントツリー(亜種?)と同様の形式です.
定義されている構造体
- static_segtree_Add_At<T> t
参考:LeetCode Weekly Contest 217 3問目 - Minimum Moves to Make Array Complementary [1674]
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 void 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_nw<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) の数を求める.
2次元領域木,2d range tree(抽象化版)
2次元平面内の長方形領域に含まれる点の重み w1, w2, ..., wn に対して f(w1, w2, ..., wn) を求める(可換かつモノイド).
rangeTree2d_pf<S,T1,T2> t; で定義する.
領域量は O(N log N) でワーキングメモリを rangeTree2d 以上に使うので注意(Fenwick treeの代わりにSegment treeで管理します).必要ならば,//working_memory=300m などしておくと良い.
- inline void t.build(int nn, T1 dd1[], T2 dd2[], S ww[] = NULL, void **mem = &wmem) : 点の数 nn,i番目の点の座標 (dd1[i], dd2[i]) で重みが ww[i].ww[] = NULL の場合は点の重みは初期化しません.時間計算量は O(N log N).
- inline void t.change(T1 x, T2 y, S v) : 点 (x,y) の重みを v にする.ただし,点 (x,y) が存在することを仮定している.2つ以上存在したら壊れるかもしれない.時間計算量は O((log N)^2).
- inline void t.add(T1 x, T2 y, S v) : 点 (x,y) の重みを +v する.ただし,点 (x,y) が存在することを仮定している.2つ以上存在したら壊れるかもしれない.時間計算量は O((log N)^2).
- inline void t.setDefault(const S val) : queryにおいて対応する点が存在しない場合に返す値defvalをvalにセットします.
- inline S t.query(T1 x1, T1 x2, T2 y1, T2 y2) : x1 ≤ x < x2, y1 ≤ y < y2 なる点 (x,y) の重みに対して f(w) を求める.そのような点が存在しない場合はsetDefaultで設定したdefvalを返す(呼び出していないなら何を返すか不定).時間計算量は O((log N)^2).
関数 f を表す関数
S rangeTree2d_pf_func(S a, S b)
をグローバルに定義しておいてください.
例えば,rangeTree2d_pf<int,int,int> t; において,最小値を求める場合は,
int rangeTree2d_pf_func(int a, int b){
return min(a,b);
}
とすれば良く,この場合は t.setDefault(int_inf) などを呼び出しておくと良いです.
f(x,y,z,w) = f(x,f(y,f(z,w))) みたいに計算されます.
グラフ(構造体)
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.getDistPairMatrix(int k, int ind[], T **d, void *mem = wmem) : d[i][j] = 「ノードind[i]とノードind[j]の最短距離」を計算して代入します.配列indのサイズがkです.辿り着けない場合は-1にします.dはメモリ確保済みであること.BFSをk-1回程度行います.参考:AtCoder Beginner Contest 190 E問題 - Magical Ornament.
- 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 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.longestPath_length(void *mem = wmem) : 有向グラフに対して最も長いパスの長さを求めます.閉路があるなら-1を返します.参考:AtCoder Beginner Contest 135 F問題 - Strings of Eternity.
- 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.anUndirectedCycle(int res[] = NULL, void *mem = wmem) : 無向グラフに対してサイクルを1つ求める.戻り値はサイクルの長さ.サイクルが存在しなければ-1を返す.長さ3の場合の res の中身は {3,9,0,3} の用な感じで長さ+1個の要素を使う.時間計算量は $O(N+M)$.何を返すかわからない(一応極小なものを返す気がする).
- 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の色を返します.
無向木に対する全方位DP(Rerooting)
- template<class V> void Rerooting(V res[], void *mem = wmem)
wgraphにあるRerootingでECostがなくなって,関数の名前の最初にwがなくなったものです.
wgraphのほうの説明を見てください.
例えば,グローバルに
struct rval{ int sz; ll dist; };
void RerootingId(rval &a){ a = {0, 0}; }
rval RerootingMerge(rval a, rval b){ return {a.sz + b.sz, a.dist + b.dist}; }
rval RerootingEdge(rval a, int EFrom, int ETo){ return a; }
rval RerootingNode(rval a, int Nind){ return {a.sz+1, a.dist+a.sz}; }
を用意しておくと,
szにそのノードを根とする部分木のノード数=全体のノード数
distにそのノードから他のノードまでの距離の総和
を求めることになります.
{
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にはならない).ワーキングメモリを使います.
- template<class S> void g.getDist01(int root, S res[], void *mem=wmen) : 枝の重みは0または1のみと仮定し,ノードrootから各ノードまでの距離を01BFSで求めます.辿り着けないノードまでの距離は -1 を返します.
- int g.getDist01(int a, int b, void *mem=wmen) : 枝の重みは0または1のみと仮定し,ノードaからノードbまでの距離を返します.
- T g.MST_Prim_cost(void *mem = wmem) : 無向グラフだと仮定して,最小全域木の重み和を返す.Prim法を使う.
- int g.MST_Prim_cost(T &res, void *mem = wmem) : 無向グラフだと仮定して,最小全域木の重み和をresに代入して求める.戻り値は全域木が存在するなら1,存在しないなら0を返す.Prim法を使う.
無向木に対する全方位DP(Rerooting)
- template<class V> void Rerooting(V res[], void *mem = wmem)
枝は向きがあると思います(向きによって重みが違っても良いですが,無向木に対して枝を両方の向きに分裂させた形のグラフのみが対象です).
ノードkに対する値(型はV)をn(k),枝kに対する値(型はV)をe(k)と書くことにします.
枝kに対する値は,枝kの重み(g.cost)がECost,枝kはEFromからEToに向かう枝として,e(k) = wRerootingEdge(n(Eto), ECost, EFrom, ETo) で定義されます.
ノードkに対する値は,ノードkから他のノードに向かう枝をe1, e2, ..., em としたとき,
tmp = f(e1, e2, ..., em)
として
n(k) = wRerootingNode(tmp, k)
で定義します.
ただし,f は f(a, b, c) = f(a, f(b, c)) = f(f(a, b), c) などを満たし,更に(現状は)可換と仮定します.
fを表す関数を wRerootingMerge で指定します.
また,f(e, a) = a なる単位元 e を設定する関数 wRerootingId も用意しておきます.
最終的には,全ノードに対する n(k) の値を res[k] に代入して返します.
例えば,グローバルに
struct rval{ int sz; ll dist; };
void wRerootingId(rval &a){ a = {0, 0}; }
rval wRerootingMerge(rval a, rval b){ return {a.sz + b.sz, a.dist + b.dist}; }
rval wRerootingEdge(rval a, T ECost, int EFrom, int ETo){ return {a.sz, a.dist + a.sz * ECost}; }
rval wRerootingNode(rval a, int Nind){ return {a.sz+1, a.dist}; }
を用意しておくと(Tはintを想定,wgraph<T>の場合),
szにそのノードを根とする部分木のノード数=全体のノード数
distにそのノードから他のノードまでの距離の総和(costが距離を表します)
を求めることになります.
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, T val) : ノードuの値に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やら色々呼び出して辻褄合わせたりするので多分遅め).参考:第四回 アルゴリズム実技検定 M問題 - 筆塗り.
- 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.malloc(int N, int init_flag) : malloc(N) をした後,init_flag が 0 でなければ init(N) を呼び出します.
- flow.walloc(int N, void**mem = &wmem) : ノード数 N のグラフを確保できるメモリを確保(枝の分のメモリは確保しない)
- flow.walloc(int N, int init_flag, void**mem = &wmem)
- 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, CT clim = 0) : ノード st からノード ed までflowを流して,流れた量をfresに,コストをcresに代入する.ただし,最大で流量flimまでしか流さない.flim=-1なら流せるだけ流す,flim=-2ならコストがclim未満の間だけ流す.
参考: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(T a) : 恒等的にaの多項式(定数関数)
- 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 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): 種をセット
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
Timer2 構造体.
Timer2 timer; などで宣言して上記と同じように使用可能(Timer 構造体と使い方は全く一緒).
std::chrono::high_resolution_clock::now() を使用する.
- string toLower(const string S) : 各文字に tolower() をかましたものを返す.
- string toUpper(const string S) : 各文字に toupper() をかましたものを返す.
string S = "HoGe@Piyo";
wt(toLower(S).c_str()); // hoge@piyo
wt(toUpper(S).c_str()); // HOGE@PIYO
- template<class T> int isSorted(int N, const T A[]) : 長さ N の配列 A[] はソート済みであれば 1,そうでなければ 0 を返す.ソート済みというのは A[i-1] <= A[i] が任意の i = 1, 2, ..., N-1 について成り立つこと.
- template<class T> vector<vector<T>> Rot90(vector<vector<T>> a) : 時計回りに90度回転した盤面を返します
- template<class T, class T1, class T2> inline void swapV(T &a, T1 x, T2 y) : a が x なら y に,y なら x に変更します.どちらでもないなら変更しません.
- 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) ぐらいの尺取法.
- template<class T, class S> ll cntSubarrayFreq(int N, T A[], S lim[], void *mem = wmem) : 尺取法により条件を満たす空で無い連続部分列の数を返します.条件:部分列に含まれている値vの個数がlim[v]個以下.lim[A[i]]にはアクセスできる必要があり,それ以外のlimの要素は使用されません.
- template<class T, class S, class S2> ll cntSubarrayDistinct(int N, T A[], S cost[], S2 lim, void *mem = wmem) : 尺取法により条件を満たす空で無い連続部分列の数を返します.条件:部分列に1個以上含まれている値vに対してcost[v]の和がlim以下.特にcostが常に1なら登場する種類数がlim以下.cost[A[i]]にはアクセスできる必要があり,それ以外のcostの要素は使用されません.
- template<class T, class S, class S2> int maxSubarrayDistinct(int N, T A[], S cost[], S2 lim, void *mem = wmem) : cntSubarrayDistinctと同じ状況において,最も長い連続部分列の長さを返します.該当する部分列がなければ0を返します.
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を求める
- XOR[型][範囲](式): XOR(ビットごとの排他的論理和)を求める
- OR[型][範囲](式): OR(ビットごとの和)を求める
- AND[型][範囲](式): AND(ビットごとの積)を求める.ただし,範囲が空の場合,現状では0を返す(非直感的).
- 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-1 まで(両端含む)動かします.
変数の型を指定する場合は,変数名の前につけてください.
例えば (i,j),a,b において i は int 型だけど,j を ll 型にする場合は (i, ll j),a,b としてください.
型において「@ 条件」をつけると条件を満たすもののみ処理します.
また,「$
値」をつけると範囲が空だったり,条件を満たすものが存在しない場合に,値を代入するようになります.
(値を指定しない場合は,それぞれの演算,型を考えて,単位元的なものを代入します)
min[i,0,n @ a[i] >= 10 $
0](a[i])
で配列a[i]の中の10以上の要素の中での最小値を求めます.該当する要素がないなら0にします.
min[i,0,n @ a[i] >= 10](a[i])
の場合,a[i]がint型で該当する要素がないなら2147483647になります.
また,配列に対する 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, void *mem = wmem): N が素数なら 1,そうでないなら 0 を返す.
- 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, class R1, class R2> int Factor(T N, R1 fac[], R2 fs[], void *mem = wmem): 素因数と重複度を求める.下記の例を参照.facは異なる素因数,fsはその素因数を何個持つか.Tはint, llなどの整数.
- template<class T, class R> int Factor(T N, R fac[], void *mem = wmem): 相異なる素因数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T> int Factor(T N, void *mem = wmem): 相異なる素因数の個数を求める.Tはint, llなどの整数.
- template<class T, class R> int FactorM(T N, R fac[], void *mem = wmem): 重複を含めて素因数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T, class R> int FactorM(T N, void *mem = wmem): FactorMでfacが要らない場合(素因数の数だけ求めれば良い場合).
- template<class T, class R> int Divisor(T N, R dv[], void *mem = wmem): 約数を求める.下記の例を参照.Tはint, llなどの整数.
- template<class T> ll DivisorSum(ll N, void *mem = wmem): 約数の和を求める.
- template<class T> T EulerPhi(T n, void *mem = wmem): オイラーのファイ関数(totient関数).gcd(i,k)=1なる1以上k以下のiの個数.
- template<class T> int Moebius(T n, void *mem = wmem): メビウス関数の値を求める.nが平方数で割り切れるなら0を返し,そうでないなら素因数の数を $k$ として $(-1)^k$ を返す.
isPrime, Factor, FactorM, Divisor, DivisorSum, EulerPhi, Moebius においては,Nが64ビットに収まるならば,ミラーラビン,ポラードローなどを利用します(現状).
64ビットに収まらない場合は愚直な実装です.
内部で128bit整数 __uint128_t 型を使っています.
- template<class T> void minFactorList(int N, T res[]): k=0,1,...,N-1に対して res[k] = [kの最小の素因数],を計算します.特にkが素数の場合 res[k] = k になります.res[0]=res[1]=0.
- template<class T> void maxFactorList(int N, T res[]): k=0,1,...,N-1に対して res[k] = [kの最大の素因数],を計算します.
- template<class T> void FactorList(int N, T res[], void *mem = wmem): k=0,1,...,N-1に対して res[k] = [kの相異なる素因数の数],を計算します.Factor(k)の戻り値を列挙.
- template<class T> void FactorMList(int N, T res[], void *mem = wmem): k=0,1,...,N-1に対して res[k] = [kの重複を含めた素因数の数],を計算します.FactorM(k)の戻り値を列挙.
- template<class T> void EulerPhiList(int N, T res[], void *mem = wmem): k=0,1,...,N-1に対して res[k] = EulerPhi(k),を計算します.
xxxList系の関数は特に記述がなければだいたい O(N log N) 時間以内ぐらいのつもり.
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
- ll cntPrime(ll n, void *mem = wmem) : n 以下の素数の個数を返します.現状O(n^0.75)だと思う,多分.
- template<class T> T sumPrime(ll n, void *mem = wmem) : n 以下の素数の和を返します.現状O(n^0.75)だと思う,多分.res = sumPrime<__int128_t>(n); や res = sumPrime<Modint>(n); で使用する.
- ull powmod(ull a, ull b, ull m) : a の b 乗を mod m で求める.a は 32bit 以内の整数.
- template<class T, class P, class M> T PowMod(T a, P b, M m) : a の b 乗を mod m で求める.基本的にTの型で演算をやる.Tの型の変数にintの1を代入すると単位元になることが必要(T=Polynomial<Modint>などはOK).bは,現状,負は未対応(b=0と同じ結果).
- 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は非負で最小となるようなものを返す(b=0でxが負しか存在しない場合はそれを返す,yが複数存在する場合はy=0を返す).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のように配列を指定可能です.
- XOR(): 引数のビットごとの排他的論理和を返します.rd, wtのように配列を指定可能です.
- OR(): 引数のビットごとの論理和を返します.rd, wtのように配列を指定可能です.
- AND(): 引数のビットごとの論理積を返します.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
min(), max() については( //no-min() などのフラグを使わなければ)標準のC++の min(), max() を上書きして使えなくなる(両立できなくなる)ので,
std::min(), std::max() と std:: をつけると cLay の機能でなく,C++ のmin(), max() を使います.
int x = -10;
ull y = 4;
ll z;
z = min(x,y) * 1000000000;
wt(z); // -10000000000 (min(x,y) はll型の-10になる; 一番桁数が大きいものの符号付きになる)
z = std::min(x,y); // C++のminでは型が揃ってないのでコンパイルエラー
z = min({1,2,3}); // cLay の機能ではコンパイルエラー
z = std::min({1,2,3}); // C++のminでは z=1 になる
- 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)$.
- template<class T, class S> auto maxRectArea(int N, T H[], S W[], void *mem = wmem) : H[i], W[i]は非負.高さH[i],幅W[i]の長方形が並んでいるとき,その領域の内部に含まれるような最大の長方形の面積を返す.つまり,i, j が動くときの,max( (W[i]+...+W[j]) * min(H[i], ..., H[j]) ) を返す.いわゆるヒストグラム中の最大長方形.幅が1固定の場合は下の関数も可.戻り値は H[0] * S[0] の型.
- template<class T> T maxRectArea(int N, T H[], void *mem = wmem) : 上の関数において全てのiに対して W[i] = 1 の場合の答えを返す.
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.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.H_s(ll a, ll 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.Catalan(int n, int m, int k) : 右にn回,上にm回進む格子路において,右下の角から距離k以下の部分が通れない場合の経路の数を求めます(画像).n=m=kの場合普通のカタラン数に,kが0以下の場合は二項係数に,kがmin(n,m)より大きい場合は0に,その他の場合 C(n+m, m) - C(n+m, k-1) になります.
- T comb.Catalan_s(ll n, ll m, ll k)
- 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, int md) : フィボナッチ数の第 n 項を mod md (Fn mod md)で求めます.mdを省略するとmd = MDが指定されます.O(log n) 時間.
- int Fibonacci_mod(ull n) : Fn mod MD を返します(MD は define MD で定義したもの,定義してなければ10^9+7)
- int Fib_mod(ull n) : Fn mod MD を返します.行列べき乗の要素に対応するものを前計算しておきます(たくさん呼び出す場合は多少 Fibonacci() より高速です)
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 T1, class T2> int LinearEquationMod2(int R, int C, T1 **A, T2 *b, void *mem = wmem) : mod2でAx=bの解の個数を求める.解が存在しなければ-1を返し,解が2^k個ならばkを返す.A,bは要素が全部0か1.行列AはR行C列のベクトルbはR次元.
- template<class T> int LinearEquation(int R, int C, T **A, T *b, T *x, void *mem = wmem) : 連立一次方程式 Ax=b の解を1つ求める.解が存在しなければ -1 を返し,存在する場合は解空間の次元(任意定数の個数,0以上の整数)を返す.実数の場合は,絶対値がこの値以下になったら 0 とみなすグローバル変数 double LinearEquation_EPS = 1e-9; があるので書き換えても良い.T は Modint, Mint, modint, mint (ただし法は素数) や double, float あたりを想定.
double **a, *b, *x;
walloc2d(&a, 3, 2);
walloc1d(&b, 3);
walloc1d(&x, 2);
writerDigit_double(4);
a[0][0] = 3; a[0][1] = 4; b[0] = 5.5;
a[1][0] = 3; a[1][1] = 4; b[1] = 5.5;
a[2][0] = 1; a[2][1] = 2; b[2] = 2.5;
wt(LinearEquation(3, 2, a, b, x));
wt(x(2));
は
0
0.5000 1.0000
と
double **a, *b, *x;
walloc2d(&a, 3, 2);
walloc1d(&b, 3);
walloc1d(&x, 2);
a[0][0] = 3; a[0][1] = 4; b[0] = 5.5;
a[1][0] = 3; a[1][1] = 4; b[1] = 5.6;
a[2][0] = 1; a[2][1] = 2; b[2] = 2.5;
wt(LinearEquation(3, 2, a, b, x));
は
-1
と
double **a, *b, *x;
walloc2d(&a, 3, 2);
walloc1d(&b, 3);
walloc1d(&x, 2);
writerDigit_double(4);
LinearEquation_EPS = 0.5;
a[0][0] = 3; a[0][1] = 4; b[0] = 5.5;
a[1][0] = 3; a[1][1] = 4; b[1] = 5.6;
a[2][0] = 1; a[2][1] = 2; b[2] = 2.5;
wt(LinearEquation(3, 2, a, b, x));
wt(x(2));
は
0
0.5000 1.0000
と出力されます(最後の例は,実装に依存する部分があり,将来的に多少変わる可能性もあります).
- Modint Determinant(int n, Modint **mat, void *mem = wmem) : n*nの行列matの行列式を返します
- modint Determinant(int n, modint **mat, void *mem = wmem) : n*nの行列matの行列式を返します
- Mint Determinant(int n, Mint **mat, void *mem = wmem) : n*nの行列matの行列式を返します
- mint Determinant(int n, mint **mat, void *mem = wmem) : n*nの行列matの行列式を返します
- 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 LIS_ends(int n, T a[], S res[], void *mem = wmem) : a[k]を最後に使う最長増加部分列の長さをres[k]に入れ,最長増加部分列の長さ = max(res[k]) を返す
- template<class T> int weaklyLIS_ends(int n, T a[], S res[], void *mem = wmem)
{
int N = 10;
int A[10] = {4,1,3,5,3,1,4,3,4,1};
int len, res[10];
len = LIS_ends(N, A, res);
wt(len, ":", res(N)); // 3 : 1 1 2 3 2 1 3 2 3 1
len = weaklyLIS_ends(N, A, res);
wt(len, ":", res(N)); // 5 : 1 1 2 3 3 2 4 4 5 3
}
- template<class T, class S> int coordcomp(int n, const T arr[], S res[], void *mem = wmem) : 座標圧縮.arrの各要素の大きさの順番を維持したまま小さな非負整数に変換したものをresに格納.異なる要素の数を戻り値として返す.coordcomp()の代わりにcoord_comp()も可能.$O(n \log n)$ 時間.
- template<class T> int coordcomp(int n, T arr[], void *mem = wmem) : 上において,resに結果を格納する代わりにarrに上書きする.
- template<class T, class S1, class S2> int coordcomp(int n1, const T arr1[], int n2, const T arr2[], S1 res1[], S2 res2[], void *mem = wmem) : 座標圧縮.2つの配列を連結してから処理する.異なる要素の数を戻り値として返す.coordcomp()の代わりにcoord_comp()も可能.
- template<class T> int coordcomp(int n1, T arr1[], int n2, T arr2[], void *mem = wmem)
- template<class T, class S1, class S2, class S3> int coordcomp(int n1, const T arr1[], int n2, const T arr2[], int n3, const T arr3[], S1 res1[], S2 res2[], S3 res3[], void *mem = wmem) : 座標圧縮.3つの配列を連結してから処理する.
- template<class T> int coordcomp(int n1, T1 arr1[], int n2, T arr2[], int n3, T arr3[], void *mem = wmem)
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, void *mem = wmem) : 要素数Nの配列Aの重複する要素を削除する.要素数,配列は書き換えられる.勝手にソートする.既にソート済みならsorted=1にするとソートしなくなり速くなる.
- template<class T, class S> void Unique(int &N, T A[], S B[], int sorted=0, void *mem = wmem) : 上と同じ.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に含まれる要素の和
HashMap構造体.
内部ハッシュ(開番地法)でC++のunordered_map的なものを作ります(高速かつ簡単に性能が劣化するケースが作れないようにしたいと思っていますがそうなってるかは謎).
HashMap<KEY,VAL> hs; で定義したとすると,KEY型の key を使って VAL型の変数 hs[key] を使用できます.
現在 KEY は int, unsigned, ll, ull, pair<int,int> のみに対応.
hs[key] と書いた瞬間(書き込まなくても)hs[key]の要素が作られ,全体を初期化しない限り消せません.逆に一度作ってしまえば以降はずっと同じアドレスを指します(要素数が増えてもリハッシュしたりもしません).
hs[key] の要素の初期値は不定です.
- void hs.init(int nn) : 最大nn要素ぐらいを使うとして初期化(中身を削除)します.(現在は配列サイズは 1.5 * nn 以上の最小の2の冪数ぐらいに設定しています).nn要素以上使うと急激に遅くなったり無限ループになったりします.配列の初期化でなぞるのでO(nn)時間掛けます.
- void hs.init(int nn, SVAL ini) : こっちで初期化すると,作られていない要素にアクセスした際に,勝手にiniで初期化されるようにします.
- inline bool hs.exist(const KEY a) : hs[a] が既に作られていれば true,そうでなければ false を返します.
- template<class S> inline bool exist(const KEY a, S &res) : hs[a] が既に作られていれば res に代入し true を返す.そうでなければ false を返します.
使わないもの
- HashMap() : コンストラクタ
- ~HashMap() : デストラクタ.メモリ解放します.
- void hs.expand(int nn) : 内部の配列のサイズがnn未満だったらnnに拡張します.
- void hs.free() : メモリ解放します.
- int hs.getHash(const T a) : ハッシュ関数の値を取得します.T = int, unsigned, ll, ull, pair<int,int> でそれぞれ定義されています.
参考:yukicoder No.1293 2種類の道路
参考:Codeforces Round #684 DIV1 B問題 - Graph Subset Problem
- ll inversion_range(const int N, const int A[], const int mn, const 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(const int N, const T A[], void *mem=wmem) : 要素数Nの配列A[]の転倒数を求めます.マージソートしながら数えるやつ.時間計算量は $O(N \log N)$ です.
- template<class T> ll inversion(const vector<T> &A, void *mem = wmem)
- ll inversion(const string &A, void *mem = wmem)
- template<class T> ll inversion(const int N, const T A[], const T B[], void *mem=wmem) : 要素数Nの配列A[],B[]について,Aの隣接要素をswapさせてBに一致させるときの最小のswap数を返します.不可能なら-1を返します.
{
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に上書き
また,逆に,res[i] に i を包含する j についての A[j] の和
$\displaystyle res[i] = \sum_{j \in i} A[j]$
を求める高速ゼータ変換,その逆変換のメビウス変換は以下の通り.
- template<class T, class S> void ZetaTransform2(int N, T A[], S res[])
- template<class T> void ZetaTransform2(int N, T A[])
- template<class T, class S> void MoebiusTransform2(int N, T A[], S res[])
- template<class T> void MoebiusTransform2(int N, T 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の最後の要素の値を返す
- template<class T, class S> T getClosest(multiset<T> &a, S v), template<class T> T getClosest(set<T> &a, S v) : aの要素の内でvに一番近い要素を返す.前後に同じ距離の要素があるなら小さい方を返す.
- template<class T, class S> T getClosestL(multiset<T> &a, S v), template<class T> T getClosestL(set<T> &a, S v) : aの要素の内でvに一番近い要素を返す.前後に同じ距離の要素があるなら大きい方を返す.
- template<class T, class S> T getClosest(int N, T A[], S v) : 配列Aはソート済みとする.
- template<class T, class S> T getClosestL(int N, T A[], S v)
いずれの関数も,空の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であるという意味(配列resの長さは配列Aの長さと同じ).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 T1, class T2> int isSubsequence(int As, const T1 A[], int Bs, const T2 B[]) : 配列Bが配列Aの部分列ならば1,そうでなければ0を返す.
- int isSubsequence(string A, string B)
- int isSubsequence_r(string &A, string &B)
- int isSubstring(string A, string B, void *mem = wmem) : 文字列Bが文字列Aの部分文字列ならば1,そうでなければ0を返す.内部ではKMP法を利用.
- template<class R, class T> R cntSubsequence(int As, const T A[], int Bs, const T B[], void *mem = wmem) : 部分列の数を数え上げる.配列AのからBs個の要素を選びそのままつなげるとBになるような要素の選び方のパターン数を返す.最悪O(As*Bs)時間ぐらい.より正確に,xに登場するkの数をnx(k)と書くと,Σはkに対する和として O((As+Bs) log (As+Bs) + ΣnA(k)*nB(k)) 時間.cntSubsequence<Modint>(n,a,m,b) のように呼び出す.
- template<class R, class T1, class T2> R cntSubsequence(int As, const T1 A[], int Bs, const T2 B[], void *mem = wmem)
- template<class R> R cntSubsequence(string A, string B, void *mem = wmem)
- 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法を使用.
- template<class T> void smallestSubsequenceLengthK(int N, T A[], int K, T res[], void *mem = wmem) : 配列A[]の長さKの部分列で辞書順最小のものを求める.O(N) 時間.
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.setN(int nn, T val) : setNした後に,配列の全要素をvalで初期化します.
- template<class S> void a.set(vector<S> &a) : それにします.メモリも自動で確保します.リセットされます.
- template<class S> void a.set_c(vector<S> a) : それにします.メモリも自動で確保します.リセットされます.
- template<class S> void a.set(int nn, S a[]) : それにします.メモリも自動で確保します.リセットされます.
[累積和関係]
- template<class T1, class T2> T a.getSum(T1 i, T2 j) : a[i]+a[i+1]+...+a[j] を返します.正確にはインデックスが max(i,0) から min(j,N-1) までの和を返します.i > j の場合は 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).
- int a.dHist(T x, T y) : 配列a[]の中に x 以上 y 以下の値が登場する回数を返す.
- void a.setDHist() : 基本的に使わない.
[ヒストグラム(疎な場合)]
- int a.sHist(T x) : 配列a[]の中にxが登場する回数を返す.前計算 O(n log n),各クエリ O(log n).
- int a.sHist(T x, T y) : 配列a[]の中に x 以上 y 以下の値が登場する回数を返す.
- void a.setSHist() : 基本的に使わない.
[左右の次の大小関係の要素を探す]
- int a.PrevLE(int i) : j < i かつ a[j] ≤ a[i] なる最大のjを返す.存在しなければ-1を返す.
- int a.PrevLT(int i) : j < i かつ a[j] < a[i] なる最大のjを返す.存在しなければ-1を返す.
- int a.PrevGE(int i) : j < i かつ a[j] ≥ a[i] なる最大のjを返す.存在しなければ-1を返す.
- int a.PrevGT(int i) : j < i かつ a[j] > a[i] なる最大のjを返す.存在しなければ-1を返す.
- int a.NextLE(int i) : i < j かつ a[j] ≤ a[i] なる最小のjを返す.存在しなければnを返す.
- int a.NextLT(int i) : i < j かつ a[j] < a[i] なる最小のjを返す.存在しなければnを返す.
- int a.NextGE(int i) : i < j かつ a[j] ≥ a[i] なる最小のjを返す.存在しなければnを返す.
- int a.NextGT(int i) : i < j かつ a[j] > a[i] なる最小のjを返す.存在しなければnを返す.
- void a.setPrevLE(void *mem = wmem) など : 基本的に使わない.
[その他]
- void a.sort(void) : 配列をソートします.リセットされます.
[基本的に使わない情報]
- int a.n : 配列長
- int a.mem : 配列のメモリを確保している長さ
- T *a.d : 生配列
Arr2d 構造体.
以下は,Arr2d<T> a; を想定.
[特殊な話]
int n1, n2; Arr2d<ll> a;
rd(n1, n2, a(n1,n2));
と読み込むことができます.
rd(a(n1,n2)); で読み込んだときには,読み込む直前に a.malloc(n1,n2); が走って勝手にメモリ確保と配列サイズが指定されます.
a[0][0], ..., a[n1-1][n2-1] で普通に要素にアクセスできます.
[コンストラクタ・メモリ関係・初期化など]
- Add2d() : コンストラクタ.変数の初期がぐらいしかしません.
- Add2d(int nn1, int nn2) : コンストラクタ.
- ~Add2d() : デストラクタ.メモリの開放などをします.
- void a.constructor(), void a.constructor(int nn1, int nn2) : コンストラクタと同じ内容を強制的に呼び出す.
- void a.destructor(), void a.free() : デストラクタと同じ内容を強制的に呼び出す.
- void a.reset() : リセット.全ての機能において前計算をしていない状況にします.配列の中身をいじったら呼び出すと良いです.
- void a.malloc(int nn1, int nn2) : n個のデータの領域を確保し(すでにn以上のメモリを確保している場合は何もしません),配列長をnnに変更します.リセットされます.
- void a.setN(int nn1, int nn2) : mallocと同じです.
- void a.setN(int nn1, int nn2, T val) : setNした後に,配列の全要素をvalで初期化します.
- template<class S> void a.set(vector<vector<S>> & a) : それにします.メモリも自動で確保します.リセットされます.
- template<class S> void a.set_c(vector<vector<S>> a) : それにします.メモリも自動で確保します.リセットされます.
- template<class S> void a.set(int nn1, int nn2, S **a) : それにします.メモリも自動で確保します.リセットされます.
[累積和]
- template<class T1, class T2, class T3, class T4> T getSum(T1 r1, T2 c1, T3 r2, T4 c2) : 長方形領域の和を返す.厳密には g[i][j] (max(0, r1) ≤ i ≤ min(n1-1, r2) かつ max(0, c1) ≤ j ≤ min(n2-1, c2)) の和を返す.ただし,r1 > r2 または c1 > c2 の場合は 0 を返す.
- T a.getSumBorder(int r1, int c1, int r2, int c2) : 長方形領域の周の要素の和を返す(インデックスが範囲外の場合うまく動かない).
- void a.setSum() : 基本的に使わない.
[斜め累積和]
- T a.getSum45(int r1, int c1, int r2, int c2) : g[r1][c1], g[r2][c2] を対角になるような斜め45度傾いた長方形領域の要素の和を返す.はみ出る部分は0として扱う.O((n1+n2)^2)の計算,メモリを使用するので,縦長・横長の場合には注意.(r,c)→(c-r,c+r)に座標変換して処理する感じ.
- T a.getSum45Border(int r1, int c1, int r2, int c2) : 上の長方形領域の周の要素の和を返す.
- void a.setSum45() : 基本的に使わない.
[縦横に連続する数]
- int a.ConstLenLeft(int i, int j, T val) : a[i][j]から左(a[i][j-1]方向)に向かってvalが何個連続しているかを返します.前計算O(n1*n2),各クエリO(1).
- int a.ConstLenLeft(int i, int j) : ConstLenLeft(i, j, a[i][j]) と同じです.
- int a.ConstLenRight(int i, int j, T val) : a[i][j]から右(a[i][j+1]方向)に向かってvalが何個連続しているかを返します.
- int a.ConstLenRight(int i, int j)
- int a.ConstLenUp(int i, int j, T val) : a[i][j]から上(a[i-1][j]方向)に向かってvalが何個連続しているかを返します.
- int a.ConstLenUp(int i, int j)
- int a.ConstLenDown(int i, int j, T val) : a[i][j]から下(a[i+1][j]方向)に向かってvalが何個連続しているかを返します.
- int a.ConstLenDown(int i, int j)
- void a.setConstLenLeft() : 基本的に使わない.ConstLenLeft() 系の強制的に前計算をします.
- void a.setConstLenRight() : 基本的に使わない.
- void a.setConstLenUp() : 基本的に使わない.
- void a.setConstLenDown() : 基本的に使わない.
[基本的に使わない情報]
- int a.n1, a.n2 : 配列長
- int a.mem1, a.mem2 : 配列のメモリを確保している長さ
- 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とする(通るマスの数の最小化).
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
FFT単体を行う関数は以下の通りです(上手く使いまわしてFFTの回数を減らす場合はこちら).配列の長さは2の冪でなければいけません.
fft_pnt構造体は struct fft_pnt{ double x, y; } で x + yi という複素数を表すもので +-* の3種の演算子が定義されています.
FFT,逆変換ともに定数倍を考慮していません.配列の長さ nでFFTして畳み込みを計算する場合は,FFTして,要素ごとに掛け算して,逆変換した後,各要素を 1/n 倍する必要があります.
- void fft(int n, fft_pnt x[], void *mem = wmem)
- void fftinv(int n, fft_pnt x[], void *mem = wmem)
- void fft(int n, mint x[], mint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fftinv(int n, mint x[], mint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fft(int n, Mint x[], Mint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fftinv(int n, Mint x[], Mint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fft(int n, modint x[], modint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fftinv(int n, modint x[], modint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fft(int n, Modint x[], Modint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
- void fftinv(int n, Modint x[], Modint root = MD_PRIMITIVE_ROOT, void *mem = wmem)
参考:TOKI Regular Open Contest #17 H問目 - Guitar Gift
高速Walsh-Hadamrd変換を用いたxor convomution(xor畳み込み).
$\displaystyle R[k] = \sum_{k = i \oplus j} A[i] B[j]$
となる配列を求めます($\oplus$ はbitwise xor).
配列の型は現在 Modint, modint, Mint, mint, double ぐらいのみを想定.
- template<class T1, class T2, class T3> void xorConvolution(int As, T1 A[], int Bs, T2 B[], int Rs, T3 R[], void *mem = wmem)
高速Walsh-Hadamrd変換自体は以下の通り.
配列のサイズは2の冪である必要があります.xor畳み込みをする場合,自力で最後に各要素を配列サイズnで割る必要があります.
配列は上書きします.
- template<class T> void HadamardTransform(int N, T A[])
高速ゼータ変換を用いたand convolution(and畳み込み)とor convolution(or畳み込み).
$\displaystyle R[k] = \sum_{k = i\ \mathrm{and}\ j} A[i] B[j]$
$\displaystyle R[k] = \sum_{k = i\ \mathrm{or}\ j} A[i] B[j]$
となる配列を求めます.
配列の型は現在 Modint, modint, Mint, mint, double ぐらいのみを想定.
- template<class T1, class T2, class T3> void andConvolution(int As, T1 A[], int Bs, T2 B[], int Rs, T3 R[], void *mem = wmem)
- template<class T1, class T2, class T3> void orConvolution(int As, T1 A[], int Bs, T2 B[], int Rs, T3 R[], void *mem = wmem)
and convolutionの方は,ZetaTransform2()を行い,要素ごとにかけた後,MoebiusTransform2()を行えば得られますが,要素数が2の冪でないときに注意.
or convolutionの方は,ZetaTransform()を行い,要素ごとにかけた後,MoebiusTransform()を行えば得られますが,要素数が2の冪でないときに注意.
グラフの隣接リスト(隣接配列?)のようなものを作ります.
時間ごとにイベントを処理する場合などの整理などにも多分便利.
- 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, void *mem = wmem) : 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-" を返します.
- 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してない.かなり怪しい.
二次元幾何.まだだいぶ怪しいです(作りかけだと思ったほうが良い).
Point2d<T,S,F> p; で定義したとします.
Tは座標の型,Sは座標の二乗以上のオーダーになる場合の型,Fは整数計算で収まらない場合の型です.
標準的には,できるだけ整数で計算する場合は Point2d<int,ll,double> p; で,そうじゃない場合は Point2d<double,double,double> p; で.
これ以内の差ならば同じとみなす(場合がある)定数を型ごとに指定できます.
(現状の)標準では,double型,long double型は1e-10,float型は1e-4になっています.
{Point2dEPS<double> tmp; tmp.set(1e-13);} で 1e-13 になります.
rd(), wt() に対応(単にx,yを並べて出力)してると思います.
s = sum(a(n)) は未対応です.s = sum[i,0,n](a[i]); はできます.
コンストラクタ系
- Point2d() : 点(0,0)
- Point2d(T a) : 点(a,0)
- Point2d(T a, T b) : 点(a,b)
- void p.set(T a, T b) : 点(a,b)にセットします
オペレータ(メンバ関数)
- Point2d<T,S,F> &operator+=(Point2d<T,S,F> a)
- Point2d<T,S,F> &operator-=(Point2d<T,S,F> a)
- Point2d<T,S,F> operator+(Point2d<T,S,F> a)
- Point2d<T,S,F> operator-(Point2d<T,S,F> a)
オペレータ(その他)
- template<class T, class S, class F> inline Point2d<T,S,F> operator*(T a, Point2d<T,S,F> b)
メンバ関数
- inline F p.dist(void) : 原点から点pまでの(ユークリッド)距離を返す
- inline F p.dist(Point2d<T,S,F> a) : 点aからpまでの(ユークリッド)距離を返す
- inline S p.dist2(void) : 原点から点pまでの距離の2乗を返す
- inline S p.dist2(Point2d<T,S,F> a) : 点aからpまでの距離の2乗を返す
- inline F p.arg(void) : 偏角(だいたいatan2(y,x))を返す.(-pi, pi] で返すはず.原点の場合は0を返すはず.
- inline F p.arg(Point2d<T,S,F> a) : 偏角のaから差分を返す.大体 p.arg() - a.arg() mod 2*pi を (-pi, pi] で返す.
その他関連関数
- template<class T, class S, class F> S InnerProd(Point2d<T,S,F> a, Point2d<T,S,F> b) : 内積を返す
- template<class T, class S, class F> S CrossProd(Point2d<T,S,F> a, Point2d<T,S,F> b) : 三角形の符号付き面積の2倍を返す
- template<class T, class S, class F> S CrossProd(Point2d<T,S,F> c, Point2d<T,S,F> a, Point2d<T,S,F> b) : 三角形の符号付き面積の2倍を返す
- template<class T, class S, class F> void xysortA(int N, Point2d<T,S,F> A[]) : 要素数Nの配列Aをソートする.ソートの順番はx座標の小さい順.x座標が同じ点はy座標が小さいものが先にくる.
- template<class T, class S, class F, class D> void xysortA(int N, Point2d<T,S,F> A[], D ind[], void *mem = wmem) : (A[i], ind[i]) のペアを xysortA の基準でソートする.
- template<class T, class S, class F> void argsortA(int N, Point2d<T,S,F> A[], void *mem = wmem) : 偏角順にソートする.大体atan2(y,x)が小さい順になる.
- template<class T, class S, class F, class D> void argsortA(int N, Point2d<T,S,F> A[], D ind[], void *mem = wmem) : (A[i], ind[i]) のペアを argsortA の基準でソートする.
- template<class T, class S, class F> int CCW(Point2d<T,S,F> a, Point2d<T,S,F> b, Point2d<T,S,F> c) : 点の進行方向を調べる.a → b の線分に対して,cが反時計回りの方向にある場合1 [三角形の符号付き面積の符号],a → b の線分に対して,cが時計回りの方向にある場合-1 [三角形の符号付き面積の符号],c, a, b の順番で一直線上にある場合2,a, b, c の順番で一直線上にある場合-2,a, c, b の順番で一直線上にある場合0を返す.
- template<class T, class S, class F> int CCW(Point2d<T,S,F> b, Point2d<T,S,F> c) : CCW(a,b,c) で a が原点の場合.
- template<class T, class S, class F> int ConvexHull(int N, Point2d<T,S,F> A[], Point2d<T,S,F> res[], void *mem = wmem) : 凸包を返す.戻り値は凸包に含まれる点の数.res[0],res[1],...,res[s]の順番に反時計回りに凸包の頂点が代入される.ただしres[0]=res[s].結果に関わらず配列resは要素数N+1以上のサイズにすること.凸包の頂点数は最小にする.つまり,一直線上に並ぶ点の端以外は答えに含めない.
- template<class T, class S, class F> int ConvexHull_sorted(int N, Point2d<T,S,F> A[], Point2d<T,S,F> res[]) : xysort済みの場合はこちら.
- template<class T, class S, class F> S PolygonArea2(int N, Point2d A[]) : 自己交差のない多角形の面積の2倍を求める.時計回りの場合は面積は負となる.
- template<class S, class T> inline T RoundUp(T a, S b) : a以上の最小のbの倍数を返す.a, bは整数.
- template<class S, class T> inline T RoundDown(T a, S b) : a以下の最大のbの倍数を返す.a, bは整数.
- 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> T TSP_path(int n, T **dist, void *mem = wmem) : どこから出発してどこで終わっても良い場合のTSP.参考:AtCoder Beginner Contest 190 E問題 - Magical Ornament.
- template<class T> T TSP_path_s(int n, T **dist, int s = 0, void *mem = wmem) : sから出発してどこで終わっても良い場合のTSP.参考:第三回 アルゴリズム実技検定 M問題 - 行商計画問題.
- int graph_minColor(int N, int **mat, void *mem = wmem) : 無向グラフに対して彩色数(chromatic number,隣り合うノードは違う色に塗るとして最小色数)を返す.O(N 2^N) 時間ぐらい.メモリ結構(O(log N N^2)ぐらい?)使うかも.mat[i][i]は見ない,mat[i][j]=mat[j][i]でなければならない.適当なmodで計算して0かどうか判定するので間違える可能性あり.参考:AtCoder Beginner Contest 187 F問題 - Close Group.
- 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)) 時間程度.
- int xorMin(int X, int N, int A[], int bt = 31) : Aはソート済みを仮定します.min(X^A[0], X^A[1], ..., X^A[N-1]) を返します.X, A[i] は 2^bt 未満です.O(bt log N) 時間ぐらい.
- ll xorMin(ll X, int N, ll A[], int bt = 63)
- int xorMax(int X, int N, int A[], int bt = 31)
- ll xorMax(ll X, int N, ll A[], int bt = 63)
- int isValidBracket1(int N, char S[]) : Sが (, ) のみからなる文字列で括弧の対応関係が取れている場合に1,その他の場合に0を返します
- int isValidBracket2(int N, char S[], void *mem = wmem) : Sが (, ), [, ], {, }, <, > のみからなる文字列で括弧の対応関係が取れている場合に1,その他の場合に0を返します
配列の部分集合の和.
全て O(2^n) 時間ぐらい.ソートする場合は列挙しながらマージソートします.
重複を取り除く場合などで答えが少ないなら O(n*答えの数) 時間ぐらいになります.ただし,その場合はビットセットを利用したDPなどのほうが高速になる場合が多いです.
- template<class T, class S> int subsetSum(int n, T a[], S res[]) : 長さnの配列a[]の各部分集合の和を求めてresに格納する.res[mask] は mask の下からiビット目が1ならばa[i]を含んでいる.戻り値は配列resの要素数 = 2^n.
- template<class T, class S> int subsetSumS(int n, T a[], S res[], void *mem = wmem) : 結果をソートして返します
- template<class T, class S, class U> int subsetSumS(int n, T a[], S res[], U lim, void *mem = wmem) : a[]の値は非負のみと仮定して,答えがlim以下になるもののみ列挙します
- template<class T, class S> int subsetSumSD(int n, T a[], S res[], void *mem = wmem) : 結果をソートして重複要素を削除して返します
- template<class T, class S, class U> int subsetSumSD(int n, T a[], S res[], U lim, void *mem = wmem)
int n = 4, a[4] = {3,0,4,3};
int sz, res[16] = {};
rep(mask,1<<n) rep(i,n) if(mask&1<<i) res[mask] += a[i];
wt(res((1<<n))); // 0 3 0 3 4 7 4 7 3 6 3 6 7 10 7 10
sz = subsetSum(n, a, res);
wt(res(sz)); // 0 3 0 3 4 7 4 7 3 6 3 6 7 10 7 10
sz = subsetSumS(n, a, res);
wt(res(sz)); // 0 0 3 3 3 3 4 4 6 6 7 7 7 7 10 10
sz = subsetSumS(n, a, res, 6);
wt(res(sz)); // 0 0 3 3 3 3 4 4 6 6
sz = subsetSumSD(n, a, res);
wt(res(sz)); // 0 3 4 6 7 10
sz = subsetSumSD(n, a, res, 6);
wt(res(sz)); // 0 3 4 6
- template<class T, class S> void maxSubsetDP(int N, T cost[], S res[], void *mem = wmem) : 集合をちょうどk個の空でない部分集合に分割する際のコストの最大値を最小化するDP.k=1,2,...,Nに対して,res[k-1]には 2**N-1 = (i1 | i2 | ... | ik) = (i1 + i2 + ... + ik) の条件で非負整数の組 (i1, i2, ..., ik) が動くとき max(cost[i1], ..., cost[ik]) の最小値が代入される.O(n*3^n)時間.
部分和問題(subset sum)を解く系列の関数群(ナップザックも作りたいね).
入力のサイズなどから使用メモリが mem_lim を超えなさそうで,一番高速っぽいアルゴリズムを選択します.
- template<class T> T opt01SubsetSum(int N, T A[], T t, T notfound = -1, int mem_lim = sizeof(memarr)/2, void *mem = wmem) : 配列A[]のいくつかの要素の和で表せる数値の中で,t以下で最も大きいものを返します(同じ要素は1度しか使えない).存在しない場合は notfound を返します.負の数も大丈夫です.
- template<class T> T opt01SubsetSumF(int N, int F, T A[], T t, T notfound = -1, int mem_lim = sizeof(memarr)/2, void *mem = wmem) : 配列A[]のちょうどF個の要素の和で表せる数値の中で,t以下で最も大きいものを返します(同じ要素は1度しか使えない).存在しない場合は notfound を返します.負の数も大丈夫です.
以下は内部で呼び出すことを想定していますが,アルゴリズムを指定したい場合は直接呼び出して良いです.また,使用するアルゴリズムを選択する部分が重くて駄目な場合なども.
- template<class T> T opt01SubsetSum_brute(int N, T A[], T t, T notfound = -1) : 愚直な全探索.メモリ使わないのが唯一の利点か.
- template<class T> T opt01SubsetSum_mim(int N, T A[], T t, T notfound = -1, void *mem = wmem) : 半分全列挙.O(2^N/2)のつもり.
- template<class T> T opt01SubsetSum_sdp(int N, T A[], T t, T notfound = -1, void *mem = wmem) : シンプルなDP.要素が全部非負ならO(Nt)ぐらいのつもり(負の要素があるともう少し悪い).ビットを使って高速化などしてない.
- template<class T> T opt01SubsetSumF_brute(int N, int F, T A[], T t, T notfound = -1) : 愚直な全探索.
- template<class T> T opt01SubsetSumF_mim(int N, int F, T A[], T t, T notfound = -1, void *mem = wmem) : 半分全列挙.O(2^N/2)のつもり.
- template<class T> T opt01SubsetSumF_sdp(int N, int F, T A[], T t, T notfound = -1, void *mem = wmem) : シンプルなDP.要素が全部非負ならO(N^2 t)ぐらいのつもり(負の要素があるともう少し悪い).ビットを使って高速化などしてない.
- template<class T1, class T2, class T3> int LexicographicGE(int N, T1 A[], int sz, T2 available_num[], T3 res[], void *mem = wmem) : 辞書順でAと等しいかそれより大きい配列のうち辞書順で最小の配列で,availlable_numの要素のみからなる,要素数Nの配列res[]を求め,1を戻り値として返します.ただし,存在しないならresはavailable_numの要素のみからなる辞書順で最小(全て最小値)の配列にセットし戻り値として0を返します.A={4,2,6,1}, available_num={-1,3,4}の場合res={4,3,-1,-1}になります.O((N + sz) log sz)時間ぐらい.
- template<class T> void cntArrayNecessaryElement(int N, int K, T **res) : 長さ i の配列で,各要素は 0 から K-1 の K 種類のどれかで,0 から j-1 までの j 種類の要素については少なくても1回は含まれるような配列の個数を res[i][j] (i=0,1,...,N で j=0,1,...,K) に代入します. T は Modint などを想定.$O(NK)$ 時間です.
- template<class T> void cntArrayNecessaryElement_walloc(int N, int K, T ***res, void **mem = &wmem) : res のメモリを mem から確保した後に,cntArrayNecessaryElement(int N, int K, T **res) を呼び出して計算します.
- template<class T> void cntArrayNecessaryElement(int N, int K, T **res, T **cnt1, T **cnt2, void *mem = wmem) : 上と同様に,長さ i の配列で,各要素は 0 から K-1 の K 種類のどれかで,0 から j-1 までの j 種類の要素については少なくても1回は含まれるような配列を考えます.res[i][j] にはそのような配列の個数を代入します.cnt1[i][j] には条件を満たす配列を全部書き出したときに 0 から j-1 までの各要素が何回出てくるかを代入します(j=0のときは0).cnt2[i][j] には条件を満たす配列を全部書き出したときに j から K-1 までの各要素が何回出てくるかを代入します(j=Kのときは0).
- template<class T> void cntArrayNecessaryElement_walloc(int N, int K, T ***res, T ***cnt1, T ***cnt2, void **mem = &wmem)
Modint **res, **cnt1, **cnt2;
cntArrayNecessaryElement_walloc(2,3,&res,&cnt1,&cnt2); // 0, 1, 2 のみからなる長さ2の配列
wt(res[2][1]); // 5 ({0,0}, {0,1}, {0,2}, {1,0}, {2,0} の5個:0を必ず含むもの)
wt(cnt1[2][1]); // 6 (↑に出てくる0の個数)
wt(cnt2[2][1]); // 2 (↑に出てくる1の個数や2の個数)
ローリングハッシュ.
配列 A[0], A[1], ..., A[N-1] をハッシュ関数値 $\sum r^k A[k] \bmod p$ に対応させます.
p は $2^{61}-1$ で固定,r は $3^k$ (gcd(k,p-1) = 1) の中からランダムに選ばれます(配列の値の範囲(幅)が2^61以上とかだと危険です.衝突の危険も少しあります).
128ビット整数(__uint128_t 型)を使用しますので,32ビット環境などでは動きません.
struct rollingHash {ll len; ull hs;} は配列の長さ len とハッシュ関数値 hs を表す構造体です.
以下 rollingHash h; で定義したとします.
配列から rollingHash を取得するには,以下の関数を利用します.
- template<class T> rollingHash calcRollingHash(int N, T A[])
rollingHash 構造体で使える機能は以下のとおりです.
- template<class T> void h.set(int N, T A[]) : 配列 A で上書きします
- template<class S, class T> void h.change(ll ind, S bef, T aft) : A[ind] を bef から aft に書き換えてハッシュ値に反映します(配列の内容を保存してないので bef も必要です)
- void h.push_front(rollingHash a) : 配列の最初に a を挿入します(len, hs を書き換えます)
- void h.push_back(rollingHash a) : 配列の最後に a を挿入します
- void h.pop_front(rollingHash a) : 配列の最初が a だとして,その部分を削除します
- void h.pop_back(rollingHash a) : 配列の最後が a だとして,その部分を削除します
- bool h.operator==(const rollingHash a) : len, hs の両方が等しい場合に true
- bool h.operator!=(const rollingHash a)
配列に対して,その連続する部分列の rollingHash を取得するには,rollingHashSubarrays 構造体を利用します.
rollingHashSubarrays はハッシュ値の累積和を記憶しておきます.
以下 rollingHashSubarrays arr; で定義したとします.
- template<class T> void arr.set(int N, T A[]) : 中身を配列 A の内容で上書きします
- rollingHash arr.get_len(int s, int len) : 最初のインデックスが s ,長さ len の連続部分列 A[s], A[s+1], ..., A[s+len-1] のハッシュ値を返します
- rollingHash arr.get(int a, int b) : arr_len(a, b - a + 1). A[a], A[a+1], ..., A[b] のハッシュ値.
r^k を計算する必要があるとき,|k| がある程度より小さい場合は,適当に前計算したものを利用します.
大きい場合(2*10^6ぐらいを超える場合)はバイナリ法で計算します.
配列の長さが 2^63 以上,あるいは,それに近い場合は動かないと思います.
その他の情報など:
- #define ROLLING_HASH_MOD (2305843009213693951ULL) : 2^61 - 1
- int ROLLING_HASH_MEM : グローバル変数.現在の ROLLING_HASH_PW, ROLLING_HASH_IPW の配列サイズ
- ull ROLLING_HASH_BASE : r = 3^k
- ull ROLLING_HASH_IBASE : r^(-1)
- ull *ROLLING_HASH_PW : ROLLING_HASH_PW[i] = r^i
- ull *ROLLING_HASH_IPW : ROLLING_HASH_IPW[i] = r^(-i)
参考:Educational Codeforces Round 101 E問題 - A Bit Similar
ほぼ使わないもの(内部的に使っているもの)
- cLtraits_identity<hoge>::type x; で hoge 型の変数が作られます.
- cLtraits_try_make_signed<hoge>::type x; で hoge の符号付き型が存在したらその型,そうでなければhoge型の変数xを宣言します(hogeがullだとll型,hogeがdoubleだとdouble型).
- cLtraits_try_make_unsigned<hoge>::type x; で hoge の符号無し型が存在したらその型,そうでなければhoge型の変数xを宣言します(hogeがllだとull型,hogeがdoubleだとdouble型).
- cLtraits_common_type<S,T>::type x; で common_type
が符号無し整数なら符号付きに変えた型になります.
更新履歴
- 20241019-1:
【機能追加っぽいの】
kthPalindromicNumber64(), reverseNumber(), isPalindromicNumber() を追加.
TwoPointers()[][][] を追加(尺取法).
【バグ修正っぽいの】
変数宣言時のコンストラクタの引数に >?= などがあるとバグるのを修正.
wt(), sum(), min() などの中でラムダ式 auto f = [](){}; を配列と思って sum(f(2)); を sum(f[0],f[1]); に展開してバグってたのを展開しないようにしたはず.
std::min(), std::max() とmin(), max() の前に std:: があるとcLay独自の min(), max() でなく C++ 標準の min(), max() を使用するようにした.
wt(min(a(3))+1); で a の型が無視され wt の中身が int 型と解釈され,状況によってエラーになるのを修正(cLtraits系などを検出するようにした).
【ドキュメントの修正】
三項演算子の併用で,reader(), writer() が単独で文にならない場合の挙動を追記.
min(), max() についていくらか追記.
- 20240714-1:
【バグ修正っぽいの】
if(), for(), while() など制御文の () の内部とか,関数の引数とかで <?=, >?=, **=, %%=, /+= が使えない,うまく展開されないのを修正.
Point2dのInnerProd()が動かないバグを修正(配列の方のInnerProdが邪魔してた).
配列外アクセスを一部修正.
- 20240420-1:
【機能追加っぽいの】
Timer2 構造体を追加.
【バグ修正っぽいの】
powmod(), PowMod() にて a^0 mod 1 が 0 を返すように修正(1を返していた).
min(0,sum[i,0,0](0)); などが動かないのを応急処置.int a[3]; sum(a(3)); のような感じで配列の展開の形にされてたのが原因.応急処置なのでまだ動かないことがあるはず.
【その他っぽいの】
#ifdef, #endif, #else があっても動くように.
- 20240104-1:
【バグ修正っぽいの】
weightedUnionFind で重みの型が int 以外のとき動かなかったのを修正.
int i = 1 >= 2; のように変数宣言の初期化の際に = があると動かなかったの修正.
max[i,0,5](式); などで式の部分に < > があるとパースミスって動かなかったのを修正.
【その他っぽいの】
UnionFind 系でコンストラクタの mode に不正な値いれて呼び出したとき assert で落ちるようにした.
string a; wt(a.substr(1,2)); などができるように:引数を string &a から const string &a にした.
↑のついでに,vector, set, multiset などを引数に取るときにも const T & の形にした.
- 20231120-1:
【機能追加っぽいの】
startWith(), endWith() を追加.
MergeTech() を追加.
WildEQ() を追加.
【バグ修正っぽいの】
inversion_range(), inversion() の引数に const をつけた.ついでに,引数が vector や string のinversion() を追加.
- 20231031-1:
【機能追加っぽいの】
VLL で vector<long long> になるように.ついでに,VVLL, VVVLL まで追加.
変数宣言時に入力する @ で vector に対応した気がする. VI @a(10); とか.
Count() において vector 版を追加.string 版を参照渡しにした.
Slice() を追加.
PalindromeCost() を追加.
BIT_Ith() を追加(BIT_ith()の使い勝手が悪いので…).
【バグ修正っぽいの】
for(int a, b; hoge; hoge) のように for の中で2個以上変数宣言したときにバグってたのを修正.
【その他っぽいの】
arrCountVal() を Count() でも呼び出せるようにした.
- 20231016-1:
【機能追加っぽいの】
@を使用して変数宣言時に入力を読み込む機能で int @(A,B,ll C)[10]; などで順番に読み込む方法を追加.
【バグ修正っぽいの】
構造化束縛の構文(pair<int,int> f(int x){return {x,2*x};} があって auto [a,b] = f(10); みたいなのや,for(auto &[a,b] : s){hoge;} みたいなの)が動くようになったと思う.
template の <> が2回出てくる場合などに困るので,hoge < hoge > hoge < hoge > hoge が && に展開されないように.
ありがとうございます.
- 20221230-1:
【その他っぽいの】
fib_mod() を tailsさんの方法 を取り入れたりしながら高速化(Fib_mod(), Fibonacci_mod()を追加).
- 20221122-1:
【バグ修正っぽいの】
sortA_index() の引数が配列3個以上の場合に動かなかったのを修正.
【ドキュメントの修正】
wtF の引数で式を使えない旨を書いた.
- 20220506-1:
【機能追加っぽいの】
sortA() 系統を高速化(?)した.従来の sortA() は sortI() に名称変更.
coordcomp() で上書きするのと新しく配列を作るので関数を分け,一部の引数の型を変更.
cntSubsequence() の引数の一部にconstをつけた.
cntSubsequence() のstring版を追加.
Unique() の引数にワーキングメモリを取るようにした(sortA()を利用するようになった).
cLtraits_try_make_unsigned 追加.
- 20220312-1:
【機能追加っぽいの】
Arr2d 構造体の累積和 getSum() の引数が配列の範囲外の場合も動くよう(仕様を確定)にした.
Distinct() の vector 版を追加.
graph構造体の maxIndependenceSet() がまだバグってたので修正.
isSorted() を追加.
【バグ修正っぽいの】
arr2bit_int(), arr2bit_ll() の string 版で余分な template が入っていたのを修正.
if文の条件式や変数宣言時の右辺などで sum(), XOR() などが使われているときの処理を変更.今まで XOR など働かものがあった.
- 20220116-1:
【機能追加っぽいの】
arr2bit_int(), arr2bit_ll() を追加.
cntArrayForDistinct(), cntArrayForDistinct_walloc() を追加.
toLower(), toUpper() を追加.
- 20211231-1:
【機能追加っぽいの】
writer で vector, set, multiset, pair をそのまま出力できるように.
LinearEquation() を追加.
【バグ修正っぽいの】
Arr1d の getSum() の仕様を修正(範囲外は0とみなす,i > jの場合0を返す,引数の型を限定しない).
arrCountVal(), arrCountValSeqMax() の引数に const をつけた.
- 20211229-1:
【機能追加っぽいの】
arrMerge(), arrMergeD() を追加.
opt01SubsetSum(), opt01SubsetSumF(), opt01SubsetSum_brute(), opt01SubsetSum_mim(), opt01SubsetSum_sdp(), opt01SubsetSumF_brute(), opt01SubsetSumF_mim(), opt01SubsetSumF_sdp() を追加.
@[a,b]で2つに展開する機能(arraylike sentence)を追加.
【バグ修正っぽいの】
ラムダ式書くと,その中身でrepなどの構文的な機能が働かないのを暫定修正(ラムダ式の引数,for(auto a:b)の形式で定義された変数が変数と認識されないバグがまだあるはず).
- 20211024-1:
【機能追加っぽいの】
sortA_index() を追加.
unionFind系列にコンストラクタを追加.
DigitHist() を追加.
STR2int(), STR2ll() を追加.
Arr1d構造体の set() において型が違っても受け付けるようにした.また,vectorについてはコピーして渡す set_c() を追加.
Arr2d構造体の set() において型が違っても受け付けるようにした.また,vectorについてはコピーして渡す set_c() を追加.
【バグ修正っぽいの】
rollbackUnionFind に comp() がなかったのを修正.
Memoizeにおいて f_clear() の引数が壊れていたのを修正.
- 20210926-1:
【バグ修正っぽいの】
関数の戻り値が ll とかcLayの機能で省略形になっている場合,Memoizeがバグるのを修正.
//working_memory=500m とかでワーキングメモリのサイズを指定できなくなってたのを修正(20210913-1でコメント関係の修正をした際にバグらせてた).
- 20210917-1:
【機能追加っぽいの】
Modint, modint, Mint, mint に対する Determinant() を追加.
ビット演算系の畳込み xorConvolution(), andConvolution(), orConvolution() を追加.
それに伴い,高速Walsh-Hadamrd変換の関数HadamardTransform(),包含関係が逆のゼータ変換がらみ ZetaTransform2(), MoebiusTransform2() を追加.
【バグ修正っぽいの】
int Mex(int N, T A[], int sorted=0, void *mem = wmem) がバグってたのを修正.
- 20210913-1:
【機能追加っぽいの】
関数を自動的にメモ化するMemoizeを追加.
プログラム全体がメイン関数のみの場合で,唐突のブロックもない場合は,メイン関数を表す {} を省略可能にした.
変数制限と同時に読み込む@に対して,読み込んだ直後にインクリメント,デクリメントと,配列に対応.
【バグ修正っぽいの】
wt(1LL); とかで 1LL が int 型だと判定されて動かないのを暫定修正.
wt((ll)(4-2)); のように,キャストの()の後にカッコの式が続くと配列の出力に展開されてたのを暫定修正.
コメントを変な入れ方をするとバグるのを暫定修正.int /*hoge*/ @N; rep /*hoge*/ (i,N); とか.
- 20210904-1:
【機能追加っぽいの】
invDigit_r(int sz, T d[]) を追加.
LexicographicGE() を追加.
チェック付き入力 cReader_ll(), cReader_eof() を追加.
cntPrime(), sumPrime() を追加.
rangeTree2d_pf 構造体を追加.
Iroot_f(), Iroot_c(), Iroot_s(), Iroot() を追加.
【バグ修正っぽいの】
rangeTree2d<S,T1,T2> で S が Modint などのように比較演算子が定義されてなくても動くように修正.
rep(i,10) rep_perm(d,10) hoge; とか if(hoge) rep_dist(i,j,0,0) hoge; とか rep や if に続けてブロックに囲まず rep_perm や rep_dist を書くとバグってたのを修正.
- 20210816-1:
【機能追加っぽいの】
rep_dist を追加.
- 20210814-1:
【機能追加っぽいの】
polationVal(n, y, t) において,割り算の回数を削減.ただしワーキングメモリが必要になった.
cpp_int を型名として認識するようにし,cpp_int が出てきたら include, using namespace を勝手に設定するように.
cntSubsequence() を追加.
【バグ修正っぽいの】
rangeTree2d 構造体の add() の戻り値を int から void に修正.
graph 構造体の anUndirectedCycle() がグラフが連結でない場合にサイクルを発見できないことがあるのを修正.
- 20210708-1:
【機能追加っぽいの】
prodDigits() を追加.
maxSubarrayDistinct() を追加.
maxSubsetDP() を追加.
weightedUnionFind 構造体を追加.
gwaph 構造体,および,wgraph 構造体に Rerooting() を追加.
EulerPhi() を追加.
minFactorList(), maxFactorList(), FactorList(), FactorMList(), EulerPhiList() を追加.
LinearEquationMod2() を追加.
【バグ修正っぽいの】
Point2d を rd() で読み込んだときに warning が出ないように応急処置.
template<class T> template<class S> void func(T a, S, b) などとtemplateを複数並べるとバグってたのを修正.
- 20210703-1:
【機能追加っぽいの】
isPrime(), Factor(), Divisor() 関連をミラーラビン,ポラードローを利用した方法に変更.ワーキングメモリを使用するようになり引数が一部変更.
Arr1d 構造体,Arr2d 構造体で setN() で要素を初期化するバージョンを追加.
string 版の isSubsequence(), isSubsequence_r() を追加.配列版の isSubsequence() の引数をちょい変更(A[], B[] が違う型でも良くした,constをつけた).
Comb 構造体に,Catalan(n, m, k),Catalan_s(n, m, k),H_s() を追加.
sum[]() などで条件を満たすもののみ処理@,中身が空の場合の値の設定$
を追加.
maxflow 構造体でmalloc, wallocにメモリ確保しながら init もするバージョンを追加.
配列版の getClosest(), getClosestL() を追加.
BIT_parity(), BIT_parity_pm() を追加.
Point2d 構造体をとりあえず追加.
arrErase() で1要素を消す場合は,消した要素の値を返すようにした.
PowMod() を追加.
Polynomial 構造体にコンストラクタ Polynomial(T a) と Polynomial& operator=(const T a) を追加.ついでにPolynomialを一部コードを書き直した.
【バグ修正っぽいの】
個数も扱う Unique() の引数でワーキングメモリを指定できるように.
AND[](), OR[](), XOR[]() が狂ってるのを修正.
文の中や関数の引数で pair<int,int>(1,1) のようなことを書くと int,int のカンマで区切られて2つの式になったり引数が壊れたりするのを修正.
- 20210607-1:
【機能追加っぽいの】
2つの配列を一致させる最小隣接swap数を求める inversion() を追加.
RoundUp(), RoundDown() を追加.
HashMap 構造体に init(int nn, SVAL ini) を追加.
swapV() を追加.
cntSubarrayFreq(), cntSubarrayDistinct() を追加.
sumsetSum() 系のワーキングメモリの確保を微調整し,nが大きい,答えが少ない,場合にも一応使えるように.
DijkstraHeap に malloc(), walloc() するときについでに init() するバージョンを追加.
vec2arr() 系列を追加.
Rot90() を追加.
Arr2d 構造体に累積和 getSum(), getSumBorder(),斜め累積和 getSum45(), getSum45Border() を追加.
- 20210524-1:
【機能追加っぽいの】
set, multiset に対する getClosest() を追加.
XOR(), OR(), AND(), XOR[](), OR[](), AND[]() などを追加.
segtree_Change_Sum を追加.
coordcomp() に3つ配列バージョンを追加.
unionFind 構造体に comp() を追加.
VVI,VVVI,VSがそれぞれvector<VI>,vector<VVI>,vector<string> に置き換えられるように.
arrCountVal() の string 版,arrCountValSeqMax() の配列版,string版を追加.
【バグ修正っぽいの】
graph 構造体の anUndirectedCycle() で res=NULL を指定した場合にバグるのを修正.
Comb 構造体の Multinomial(int sz, int a[]) が常に1を返すバグを修正.
【その他っぽいの】
fenwick, fenwick_xor の range(a,b) で範囲外(indexが0未満,N以上)の部分は0として計算するようにした.
#pragma GCC optimize("unroll-loops"), #pragma GCC optimize("inline") を追加するようにした.
- 20210405-1:
【機能追加っぽいの】
Arr2d 構造体を取り敢えず最低限の機能で追加.
【バグ修正っぽいの】
graph 構造体の bipartite() がバグってたのを修正.
- 20210404-1:
【機能追加っぽいの】
cLtraits_identity, cLtraits_try_make_signed, cLtraits_common_type を追加.
isValidBracket1(), isValidBracket2() を追加.
【バグ修正っぽいの】
graph構造体の maxIndependenceSet(), countIndependenceSet() がバグってたのを修正.
Arr1d構造体の setPrevLE() などの戻り値の方が間違えてたのを修正(int→void).
template<typename T> ... の中身で T が型だと判定するようにした.
using T = int; で T を型として認識するように,template<T> using T = vector<T> などで T<hoge> を型として認識するようにした.
hoge<hoge>::type, hoge<hoge>::type::type, ... を型として認識するようにした.
結果の型を指定せずに sum[](), sum[][]() などを入れ子にした場合の挙動をマシにしたつもり.sum(), max() 系もバグったりするのを修正,挙動を修正.
・ull i = 3; int j = -5; で min(i,j) や min(j,i) が ll 型の-5になるように(一番でかい型の符号付きにします).
これ以前の更新履歴はこちら.