ABC162 D

問題リンク

解説をみて、 R \times G \times B j - i \neq k - j の制約を無視した時の組み合わせの数。というのが理解できなかった。

 1 \leq i < j < k \leq N があるので、これを利用してうまく求めるんだな、なんて思っていたら全く分からず。

文字列Sの中から、それぞれ違う位置にある文字を3つ取ってきたとき、小さい順に  i, j, k とすればいいのだ。

そうすると、Sの中からRGBを選んでくる組み合わせは  R \times G \times B となる。

あとは簡単で、全探索をして制約2を満たすものの数だけ引けばいい。

環境変数とシェル変数の違い

自分なりの理解をまとめておく。

環境変数

プロセスが持つ変数。

親プロセスの持つ環境変数は子プロセスへと引き継がれるが、その逆はない。1

プロセスは自分自身の環境変数を自由に書き換えることができ、例えばenvコマンドなどは環境変数を書き換えたうえで指定されたコマンドを実行するため、書き換えた環境変数が子プロセスへ引き継がれている。

シェル変数

bashなどのシェルが管理している環境変数もどき。

環境変数ではないので、子プロセスから参照できない。
C言語のgetenv関数やmainの第三引数に入る環境変数への配列にもシェル変数は含まれていなかった。

環境変数とシェル変数の違い

環境変数はOSが管理しているが、シェル変数はシェルが管理している。

全ての違いはこれに起因するもの。


  1. bashでは、PWDという環境変数が存在し、まるでcdコマンドがbash環境変数を書き換えているように見えるが、cdコマンド自体がbashの組み込みコマンドである(つまり別プロセスではない)。

重複順列と重複組み合わせ

競プロやっていたら必要になった。
自分なりに理解したことをまとめておく。

重複順列

なんてことはない。
1度選んだものをもう一度選べるというもの。

例えば、1から3の整数の選び方の順列といえば、 3!となるが、
重複順列では一度選んだ整数をもう一度選びなおすことができるため、 3^3となる。

重複組み合わせ

重複順列と同じで、1度選んだものをもう一度選べる組み合わせ。

重複順列の組み合わせ版と考えればいい。
ただ、計算方法が少し複雑。

A個の要素を重複を許してB個取り出したときの組み合わせを考えるとき、
B個のマスの間に仕切りをA-1個おき、仕切りで作られた区間をそれぞれ左からA1、A2、A3としていく。
この仕切りの置き方の通りがこの重複組み合わせの答えとなる。

仕切りもマスとして数えた、B+A-1個のマスがあるとして、どこに仕切りを設置するか考える。

この時、B+A-1 C A-1となる。

また、別の考え方として、仕切りを設置しない場所を考えることもできる。

この時、B+A-1 C B であるが、結局のところ上の式と同じようになる。

また、重複をゆする順列の考えを使用し、組み合わせの計算を行うことができる。

仕切りも含めたB+A-1個のマスの並べ方は(B+A-1)!通り。
このうち、B個の重複は無視するのでB!で割る。
さらに、仕切りの重複も無視するので(A-1)!で割る。

変数定義が関数宣言と間違われる

C、C++はローカルに関数を宣言できます。1

例えば以下のように stdio.h をインクルードしなくとも、コンパイラは警告も何も発しません。

int main() {
    int puts(const char *);
    
    puts("Hello World!");
}

ローカルに宣言された関数識別子の有効範囲はそのスコープ内ですので、別のスコープ(例えば別の関数)から使用しようとするとコンパイラに怒られます。

さて、関数宣言構文は引数識別子を括弧でくくっても構いません。
つまり以下のコードは普通にコンパイルできます。

void f(int(n)) {
    printf("%d\n", n);
}

さらに、C++では関数の引数識別子を省略してしまうことができます。

void f(int()) {
    cout << "?" << endl;
}

そして、この識別子を括弧でくくり、識別子を省略した関数宣言はローカルに記述すると以下の様な見た目になります。

int main() {
    void f(int());
}

まだわかりずらいですね。
では、構造体 point を返す関数をローカルに宣言してみましょう。

int main() {
    point f(int(), int());
}

はい。
これは、識別子を省略した関数宣言にも、point型の変数を定義しているようにも見えます。

私の環境ではこれは関数宣言と解釈されました。


  1. 定義はできません

重複を許す順列

6つのアルファベット ABBCCCの並べ方について考えます。
ただし、同じ種類のアルファベットを交換しただけのようなものは区別しません。

つまり、 B B_1 B_2としたとき、
 AB_1B_2CCC
 AB_2B_1CCC
この2つは区別されません。

 Cも同様です。

どう計算したらいいのでしょうか?

重複を取り除いていく考え方をします。

 ABBCCCのすべての文字を区別した際の並べ方は 6!です。
また、 Bのダブりと Cのダブりは 6!のすべてに対して発生しているため、 Bの並べ方である 2! Cの並べ方である 3! 6!を割ってあげます。

つまり、 \frac{6!}{2!3!}となります。

さて、次は組み合わせ(Combination)の視点から見てみましょう。

全体でアルファベットは6つの場所に置くことができます。
そのうち、 Bを置く場所は2つになり、重複は数えません。
これはそのまま組み合わせに適応できます。

 ABBCCCからBの重複を数えない並べ方は[tex: {}6 C_2]です。
また、Bを抜いた全体4から Cは3つの場所におけるので、 ABBCCCからBとCの重複を数えない並べ方は[tex: {}
6 C_2 \times {}_4 C_3]となります。

追記

はてなtex記法は一体どうなってるんです?

Unicode用語

- -
抽象文字集合 Unicodeが対象とする文字集合
符号化文字集合 抽象文字集合に非負整数の番号を付けたもの
文字符号化形式 コンピュータ中での符号化文字集合データ表現方法
文字符号化方法 文字符号化形式にエンディアンなどを決定したもの

符号化文字集合は 0x000000 ~ 0x10FFFF までの範囲で定義される。
UTF16 は 0x100000 ~ 0x10FFFF をサロゲートペアを使用して表す。

文字符号化形式は UTF8 や UTF16 、 UTF32 など。
文字符号化方法は UTF16-BE など。