ABC281参加記
5完1ペナ64分 768位
kokatsuさんのAtCoder Beginner Contest 281での成績:768位
— kokatsu (@kokatsu_) December 10, 2022
パフォーマンス:1588相当
レーティング:1258→1295 (+37) :)#AtCoder #ABC281 https://t.co/8wKee04l00
だいぶレート回復できた!
A問題
D言語のiota関数を用いて0から$N$のRangeを用意し、foreach_reverseで大きい順から出力する。
import std;
void main() {
    int N;
    readf("%d\n", N);
    auto A = iota(N+1);
    foreach_reverse (a; A) a.writeln;
}
B問題
文字列$S$の先頭と末尾が英大文字であるか確認し、先頭と末尾を消去する。 残った文字列の長さが6で先頭が0でなく全ての文字が数字であればYesを出力する。
import std;
void main() {
    auto S = readln.chomp.to!(dchar[]);
    bool isOK = true;
    if (std.uni.isUpper(S.front)) S.popFront;
    else isOK = false;
    if (std.uni.isUpper(S.back)) S.popBack;
    else isOK = false;
    if (S.length == 6) {
        isOK &= (S.front > '0');
        foreach (s; S) {
            isOK &= std.uni.isNumber(s);
        }
    }
    else {
        isOK = false;
    }
    writeln(isOK ? "Yes" : "No");
}
C問題
$N$曲の長さの合計を$S$とする。 $N$曲まで流れ終わっている時間を考慮する必要がないため$T^{\prime} = T % S$とする。 曲1から順に$T^{\prime} = T^{\prime} -$(曲の長さ)の処理をしていき、$T^{\prime} < A_{i}$を満たす$i$が曲の番号であり$T^{\prime}$が曲が流れ始めてからの時間となる。
import std;
void main() {
    long N, T;
    readf("%d %d\n", N, T);
    auto A = readln.chomp.split.to!(long[]);
    long S = A.sum;
    T %= S;
    foreach (i, a; A) {
        if (T < a) {
            writeln(i+1, " ", T);
            return;
        }
        T -= a;
    }
}
D問題
動的計画法(DP)で解く。 dp[個数][総和を$D$で割った余り] = 総和の最大値として$D$で割った余り毎の総和の最大値を管理する。 dp[$K$][0]が答えとなる。
import std;
void main() {
    long N, K, D;
    readf("%d %d %d\n", N, K, D);
    auto a = readln.chomp.split.to!(long[]);
    auto dp = new long[][](K+1, D);
    foreach (i; 0 .. K+1) dp[i][] = -1;
    dp[0][0] = 0;
    foreach (i, x; a) {
        foreach_reverse (j; 0 .. K) {
            foreach (k; 0 .. D) {
                if (dp[j][k] == -1) continue;
                long S = dp[j][k] + x;
                if (dp[j+1][(k+x)%D] < S) {
                    dp[j+1][(k+x)%D] = S;
                }
            }
        }
    }
    long res = dp[K][0];
    res.writeln;
}
E問題
RedBlackTreeを2つ用意する。 1つが$M$個の整数を昇順に並べた時の先頭$K$個を管理し、もう1つが残りの$M-K$個を管理する。
import std;
struct Number {
    long num, idx;
}
void main() {
    long N, M, K;
    readf("%d %d %d\n", N, M, K);
    auto A = readln.chomp.split.to!(long[]);
    auto rbt1 = new RedBlackTree!(Number, "a.num == b.num ? a.idx < b.idx : a.num < b.num", true)();
    auto rbt2 = new RedBlackTree!(Number, "a.num == b.num ? a.idx < b.idx : a.num < b.num", true)();
    auto nums = new long[](N);
    auto res = new long[](N-M+1);
    long S;
    foreach (i, a; A) {
        if (rbt1.length < K) {
            S += a;
            rbt1.insert(Number(a, i.to!long));
            nums[i] = 1;
        }
        else{
            auto b = rbt1.back;
            if (b.num > a) {
                S += a - b.num;
                rbt1.removeBack;
                rbt1.insert(Number(a, i.to!long));
                rbt2.insert(b);
                nums[b.idx] = 2, nums[i] = 1;
            }
            else {
                rbt2.insert(Number(a, i.to!long));
                nums[i] = 2;
            }
        }
        if (i >= M - 1) {
            res[i-M+1] = S;
            if (nums[i-M+1] == 1) {
                rbt1.removeKey(Number(A[i-M+1], i-M+1));
                S -= A[i-M+1];
                if (rbt2.empty) continue;
                auto f = rbt2.front;
                S += f.num;
                nums[f.idx] = 1;
                rbt2.removeFront;
                rbt1.insert(f);
            }
            else {
                rbt2.removeKey(Number(A[i-M+1], i-M+1));
            }
        }
    }
    writefln("%(%s %)", res);
}