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

精選過去問→AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ - Qiita, Tasks - AtCoder Beginners Selection

いろんな言語のまとめ→百花繚乱!なないろ言語で競技プログラミングをする資料まとめ - Qiita

既にあるやつ

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

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

(この記事の新規性は)ないです(たまにはブログ記事を書きたかった)

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

import std.stdio : readf, readln, writeln;
import std.string : strip;

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

readfscanfと同じように入力できます。ただし

  • int以外も全部符号付き整数は%d, 符号なし整数は%uで入力(%lldとかはつかわない)
  • \nが無いと壊れる

などの違いがあります。

readln(1行読み込み)は最後に改行文字がくっついてくるのでstrip, chompなどで取り除きましょう

なお,core.stdc.stdioをimportするとC言語scanf, printfが使えるようになります。文字列の扱いが難しいのでおすすめしません

import core.stdc.stdio;
import std.string : strip;

void main() {
    int a, b, c;
    char[] s = new char[100];
    scanf("%d %d %d %s", &a, &b, &c, s.ptr);
    printf("%d %s\n", a+b+c, s.ptr);
}

ABC086A - Product

import std.stdio;

void main() {
    int a, b;
    readf("%d %d\n", &a, &b);
    writeln(["Even", "Odd"][a*b%2]);
}

配列リテラル{1, 2, 3}じゃなくて[1, 2, 3]です

ABC081A - Placing Marbles

import std.stdio, std.string;
import std.algorithm : count;

void main() {
    string s = readln.strip;
    writeln(s.count('1'));
}

s.count(x)xの個数を数えられます

ABC081B - Shift only

import std.stdio;
import std.algorithm : map, reduce, min;
import std.string : split;
import std.conv : to;

void main() {
    readln; //ignore n
    int[] l = readln.split.to!(int[]);
    auto f(int x) { //xが何回2で割れるか返す
        int cnt = 0;
        while (x % 2 == 0) {
            cnt++;
            x /= 2;
        }
        return cnt;
    }
    writeln(l.map!f.reduce!min);
}

高階関数ってやつをやってみます

  • l : 入力した配列です
  • l.map!f : lの各要素にfを適用した配列(のようなもの)です。たとえばl=[8, 12, 40]ならl.map!f = [3, 2, 3]
  • l.map!f.reduce!min : l.map!fの各要素をminを使って畳み込みます,つまりl.map!f = [a, b, c, ...]としてl.map!f.reduce!min = min(a,b,c,... )

ABC087B - Coins

import std.stdio;

void main() {
    int a, b, c, x;
    readf("%d\n%d\n%d\n%d\n", &a, &b, &c, &x);
    int ans = 0;
    foreach (i; 0..a+1) foreach (j; 0..b+1) foreach (k; 0..c+1) {
        int y = i*500 + j*100 + k*50;
        if (y == x) ans++;
    }
    writeln(ans);
}

foreach (i; a..b)for (int i = a; i < b; i++)とだいたい同じです

ABC083B - Some Sums

import std.stdio, std.string, std.conv, std.algorithm;
import std.algorithm : sum;
import std.range : iota;

void main() {
    int n, a, b;
    readf("%d %d %d\n", &n, &a, &b);
    bool ok(int x) {
        string s = x.to!string;
        int t = s.map!"a-'0'".sum;
        return a <= t && t <= b;
    }
    writeln(iota(1, n+1).filter!ok.sum);
}
  • iota(1, n+1) : [1, 2, ..., n]とだいたい同じです。python2でいうと前者がxrangeで後者がrangeです。
  • filter!ok : これも高階関数で,ok(x) = trueとなるような要素だけ残します。
  • sum : sumを求めます(そうだね)。

つまり,[1, 2, ..., n]からokに入力するとtrueになるようなものを列挙して,それの総和を求めるというコードです。

ABC088B - Card Game for Two

import std.stdio, std.string, std.conv;
import std.algorithm : sort, each;
import std.range : array;

void main() {
    readln; //ignore n
    int[] a = readln.split.to!(int[]).sort!"a>b".array;
    int[2] res; //alice, bob
    a.each!((i, x) => res[i%2] += x);
    writeln(res[0] - res[1]);
}

eachでループと同じようなことが出来ます。

ABC085B - Kagami Mochi

import std.stdio, std.range, std.algorithm;
import std.algorithm : uniq;

void main() {
    int n;
    readf("%d\n", &n);
    int[] a = new int[n];
    foreach (i; 0..n) {
        readf("%d\n", &(a[i]));
    }
    writeln(a.sort.uniq.array.length);
}

a.sort.uniqでsortしてuniqueします。

ABC085C - Otoshidama

import std.stdio, std.range;
import std.algorithm : cartesianProduct;

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

    int ans = 0;
    foreach (p; cartesianProduct(iota(n+1), iota(n+1))) {
        int a = p[0], b = p[1], c = n-a-b;
        if (c < 0) continue;
        int x = a*10000 + b*5000 + c*1000;
        if (x == y) {
            writeln(a, " ", b, " ", c);
            return;
        }
    }
    writeln("-1 -1 -1");
}

公式ドキュメント読んでたらcartesianProductとかいうの見つけたので使います。 cartesianProduct([1, 2], [3, 4]) = [[1, 3], [1, 4], [2, 3], [2, 4]]みたいな感じっぽいです。つまり二重ループが出来ます

ABC049C - 白昼夢 / Daydream

import std.stdio, std.string, std.regex;

void main() {
    string s = readln.strip;

    auto r = regex(r"^(dream|dreamer|erase|eraser)*$");

    if (s.matchFirst(r)) {
        writeln("YES");
    } else {
        writeln("NO");
    }
}

正規表現も使えます。そう,D言語ならね。

ABC086C - Traveling

import std.stdio;
import std.math : abs;
import std.typecons : tuple;
import std.meta : AliasSeq;

bool solve() {
    int T;
    readf("%d\n", &T);
    int bt = 0, bx = 0, by = 0;
    foreach (i; 0..T) {
        int t, x, y;
        readf("%d %d %d\n", &t, &x, &y);
        int dt = t-bt;
        int di = abs(x-bx) + abs(y-by);
        if (di % 2 != dt % 2 || dt < di) {
            return false;
        }
        AliasSeq!(bt, bx, by) = tuple(t, x, y);
    }
    return true;
}

void main() {
    writeln(solve() ? "Yes" : "No");    
}

AliasSeq!(a, b, c) = tuple(d, e, f)C++のtieと同じようなこと(複数同時代入)が出来るっぽいです。