ふるつき

私は素直に思ったことを書いてるけど、上から目線だって言われる

私もAtCoderに登録したら解くべき精選過去問10問をD言語で解いた

私は時々D言語AtCoderの問題を解くことがあるので(rating 958のざこざこですが)

qiita.com

D言語でやってみました。 D言語は超絶人気バリバリメジャー言語なので当然先駆者がいます。

AtCoderに登録したら解くべき精選過去問10問をD言語で解いてみた - よすぽの日記

AtCoderに登録したら解くべき精選過去問10問をD言語で解いてみた - D言語の構文と標準ライブラリを使い倒す編 - 矮小

AtCoderに登録したら解くべき精選過去問10問をD言語で解いてみた

D言語では構文や標準ライブラリの選択肢が広く、同じ問題を説いていてもまるで異なったプログラムが生まれます。ちゃんと読んでないけど↑の場合でもそうです。私も別のプログラムになる場合があったので書いておきます。

PracticeA はじめてのあっとこーだー(Welcome to AtCoder

import std.stdio, std.conv, std.string;


void main()
{
    long a,b,c;
    string s;
    readf("%d\n%d %d\n", &a, &b, &c);
    s = readln.chomp;
    writefln("%d %s", a+b+c, s);
}

D言語競技プログラミングをやるなら入力は readlnreadf で受け取ることになりそうです。 readf が割と罠で 手元では readf("%d\n%d %d\n", a, b, c); と書いて動いていたのでずっとこの書き方をしていたのですが、AtCoder上ではこれに Warning がでて CE となります。

このときは chomp で改行文字を落としていますが途中で strip 関数のことを思い出したのでそっちを使うようになります。

ABC086A Product

import std.stdio, std.conv, std.string;


void main()
{
        long a, b;
        readf("%d %d\n", &a, &b);
        if (a*b % 2 == 0) {
                writeln("Even");
        }
        else {
                writeln("Odd");
        }
}

はい

ABC081A Placing Marbles

import std.stdio, std.conv, std.algorithm, std.string;


void main(){
        auto s = readln.chomp;
        writeln(s.count('1'));
}

algorithm に入っている count 関数は便利です。大体何でも数えられます。カウントの条件はデフォルトでは a==b で行われていますが、テンプレート引数を書き換えることによって a > b のような例えば「nより大きい」ものだけを数えることもできます。ちなみに a が配列の要素で b が引数です。

ABC081B Shift only

import std.stdio, std.conv, std.string, std.algorithm;


void main()
{
        long n = readln.strip.to!long;
        long[] xs = readln.split.to!(long[]);

        long cnt = 0;
        while(true) {
                bool flag = true;

                foreach (x; xs) {
                        if (x % 2 != 0) {
                                flag = false;
                                break;
                        }
                }

                if (! flag) {
                        break;
                }

                foreach (ref x; xs) {
                        x /= 2;
                }

                cnt++;
        }
        writeln(cnt);

}

to!(long[]) が便利で文字列の配列をlongの配列に変換できます。これを知らなかった頃は map!(to!long).array をやっていて。

foreach (ref x; xs) はまあできそうだよねと思って書いたらできてびっくりしました。PHPのような罠はないでしょう。

ABC087B Coins

import std.stdio, std.conv, std.algorithm, std.string;


void main() {
        long a,b,c,x;
        readf("%d\n%d\n%d\n%d\n", &a, &b, &c, &x);

        long cnt = 0;

        foreach (i; 0..a+1) {
                foreach (j; 0..b+1) {
                        foreach (k; 0..c+1) {
                                if (i*500 + j*100 + k*50 == x) {
                                    cnt++;
                                }
                        }
                }
        }

        writeln(cnt);
}

入力は縦に

a
b
c
x

と与えられますが、 readf に改行文字も読ませて対応できて楽です。ここで foreach を使うか for を使うかは微妙なところですが、 foreach で困らないときは foreach を使ったほうがコンパイラがよしなにしてくれるという話を聞いた気がします*1

ABC083B Some Sums

import std.stdio, std.string, std.conv, std.algorithm;


void main()
{
        long n, a, b;
        readf("%d %d %d", &n, &a, &b);

        long ans = 0;
        foreach (x; 1..n+1) {
                auto s = x.to!(string).split("").to!(long[]).sum;
                if (a <= s && s <= b) {
                        ans += x;
                }
        }
        writeln(ans);
}

123[1, 2, 3] をやるのに文字列に変換して各文字で区切ってそれぞれ数値に変換というのをやってます。普通ですね。 sum は sum する。当然 reduce もあるのでそちらを使ってもいいですけどsumするときは sum を使うのがわかりやすいと思います。

ABC088B Card Game for Two

import std.stdio, std.algorithm, std.string, std.conv, std.range;


void main()
{
        long n = readln.strip.to!int;
        auto xs = readln.split.to!(long[]);
        sort(xs);
        reverse(xs);

        long alice = xs.stride(2).sum;
        long bob = xs.drop(1).stride(2).sum;

        writeln(alice-bob);
}

D言語は range を頑張って扱いたがっていて stride([1,2,3,4], 2)[1,3] になります。 drop([1,2,3,4], 1)[2,3,4] になりそうですね。

ところで sortreverse は結構罠で、最初は auto xs = readln.split.to!(long[]).sort.reverse; って書いてたんですが、 AtCoder 上では Warning: use std.algorithm.sort instead of .sort property と怒られて CE になります。まじでこの sort どこから生えてきたんだと思いながら sort(xs); って書きました。

ちなみに algorithm に生えてる sort はテンプレート引数で比較関数を変更できて sort!("a < b") とかすれば reverse いらないし property の方と差別化できて CE ならんかったのではと思っているところです。

ABC085B Kagami Mochi

import std.stdio, std.conv, std.algorithm, std.string, std.array;

void main()
{
        long n = readln.strip.to!long;
        long[] xs = [];

        foreach (i; 0..n) {
                xs ~= readln.strip.to!long;
        }
        sort(xs);
        writeln(xs.uniq.array.length);
}

やるだけ。 uniq する前には sort しましょうというのと、 返ってくる UniqResultlength プロパティを持ってないので一回 array で配列に戻します。

ABC085C Otoshidama

import std.stdio, std.conv, std.algorithm, std.string, std.range, std.array;


void main()
{
        long n, y;
        readf("%d %d\n", &n, &y);

        for (long i = 0; i <= n; i++) {
                for (long j = 0; i+j <= n; j++) {
                        long k = n - (i + j);

                        if (i * 10000 + j * 5000 + k * 1000 == y) {
                                writefln("%d %d %d", i, j, k);
                                return;
                        }
                }
        }
        writeln("-1 -1 -1");
}

条件がややこしい気がしたので for を使いましたが 0..(n-i+1) でも良いですね。やっぱりわかりにくいか。

ABC049C 白昼夢 / Daydream

import std.stdio, std.string, std.conv, std.array, std.algorithm;

void main()
{
        string[] ts = ["dream", "dreamer", "erase", "eraser"];
        string s = readln.strip;

        while (true) {
                bool flag = false;
                foreach (t; ts) {
                        if (s.length < t.length) {
                                continue;
                        }

                        if (s[($-t.length)..$] == t) {
                                s = s[0..$-t.length];
                                flag = true;
                                break;
                        }
                }
                if (! flag) {
                        writeln("NO");
                        return;
                }
                
                if (s.length == 0) {
                        writeln("YES");
                        return;
                }
        }
}

これは yosupo さんの回答が優れてたのでそっちを見ましょう。

ABC086C Traveling

import std.stdio, std.string, std.algorithm, std.array, std.range, std.conv, std.math;


void main()
{
        long n = readln.strip.to!long();
        long[] ts = [];
        long[] xs = [];
        long[] ys = [];

        foreach (i; 0..n) {
                long t, x, y;
                readf("%d %d %d\n", &t, &x, &y);
                ts ~= t;
                xs ~= x;
                ys ~= y;
        }

        bool flag = true;
        long x = 0, y = 0, t = 0;
        foreach (i; 0..n) {
                long dx = abs(x - xs[i]);
                long dy = abs(y - ys[i]);
                long dt = ts[i] - t;           

                long v = dt - (dx + dy);
                if (v < 0) {
                        flag = false;
                        break;
                }

                if (v % 2 != 0) {
                        flag = false;
                        break;
                }

                x = xs[i];
                y = ys[i];
                t = ts[i];
        }

        if (flag) {
                writeln("Yes");
        }
        else {
                writeln("No");
        }

}

絶対 dx, dy, dt = ..., ..., ... ってする構文か何かがあると思うんですがよく知らないので愚直に書きました。

おわりに

D言語はCEしなければ良い言語なのでコードテストしましょう。

*1:ほんまか