ABC284参加記
5完5ペナ88分 1805位
約1ヶ月ぶりにコンテスト参加。 D問題に苦しめられたが何とか5完。
kokatsuさんのAtCoder Beginner Contest 284での成績:1805位
— kokatsu (@kokatsu_) January 7, 2023
パフォーマンス:1199相当
レーティング:1295→1286 (-9) :(#AtCoder #ABC284 https://t.co/37m7Sp67Wc
ギリギリ水パフォ乗らなかった。。。
A問題
D言語のforeach_reverseを用いて逆順に出力。
import std;
void main() {
int N;
readf("%d\n", N);
auto S = new string[](N);
foreach (i; 0 .. N) readf("%s\n", S[i]);
foreach_reverse (s; S) s.writeln;
}
B問題
各テストケースごとに配列$A$を受け取り、$A$のうちに奇数が何個あるかをcount関数を用いて確認する。
import std;
void main() {
int T;
readf("%d\n", T);
foreach (_; 0 .. T) {
int N;
readf("%d\n", N);
auto A = readln.chomp.split.to!(int[]);
auto res = A.count!"a % 2 == 1";
res.writeln;
}
}
C問題
Union-Findを用いて連結成分の個数を求める。
import std;
void main() {
int N, M;
readf("%d %d\n", N, M);
auto uf = new UnionFind!int(N);
foreach (_; 0 .. M) {
int u, v;
readf("%d %d\n", u, v);
uf.unite(u-1, v-1);
}
int res;
foreach (i; 0 .. N) {
if (uf.root(i) == i) ++res;
}
res.writeln;
}
/// Union-Find
struct UnionFind(T)
if (isIntegral!T) {
/// Constructor
this(T n) nothrow @safe {
len = n;
par.length = len;
cnt.length = len;
foreach (i; 0 .. len) {
par[i] = i;
}
cnt[] = 1;
}
/// Returns the root of x.
T root(T x) nothrow @nogc @safe
in (0 <= x && x < len) {
if (par[x] == x) {
return x;
}
else {
return par[x] = root(par[x]);
}
}
/// Returns whether x and y have the same root.
bool isSame(T x, T y) nothrow @nogc @safe
in (0 <= x && x < len && 0 <= y && y < len) {
return root(x) == root(y);
}
/// Unites x tree and y tree.
void unite(T x, T y) nothrow @nogc @safe
in (0 <= x && x < len && 0 <= y && y < len) {
x = root(x), y = root(y);
if (x == y) {
return;
}
if (cnt[x] > cnt[y]) {
swap(x, y);
}
cnt[y] += cnt[x];
par[x] = y;
}
/// Returns the size of the x tree.
T size(T x) nothrow @nogc @safe
in (0 <= x && x < len) {
return cnt[root(x)];
}
private:
T len;
T[] par;
T[] cnt;
}
D問題
正整数$N$に対して異なる2つの素数$p,q$を用いて$N=p^{2}q$を求める問題。 制約から$p,q$のうちどちらかは$3 \times 10^{6}$以下であることがわかる。 エラトステネスのふるいを用いて$3 \times 10^{6}$以下の素数を列挙し素数$p$ごとに以下の2個のパターンの素数$q$を考える。
- $N \% p = 0$かつ${\lfloor \sqrt{N/p} \rfloor}^{2} = N/p$を満たすときの$q = \sqrt{N/p}$
- $N \% p^{2} = 0$を満たす時の$q = N / p^{2}$
問題の制約上、条件を満たす2つの素数$p,q$は1通りしかないため$q$の素数判定を行う必要がない。
import std;
void main() {
long T;
readf("%d\n", T);
long L = 3 * 10 ^^ 6;
auto sieve = new bool[](L+1);
sieve[2..L+1] = true;
long d = 2;
while (d * d <= L) {
if (sieve[d]) {
foreach (i; iota(d*d, L+1, d)) {
sieve[i] = false;
}
}
++d;
}
auto primes = iota(L+1).filter!(i => sieve[i]).array;
foreach (_; 0 .. T) {
long N;
readf("%d\n", N);
foreach (q; primes) {
if (N % q == 0) {
long M = N / q;
long S = M.to!real.sqrt.floor.to!long;
if (S * S == M) {
writeln(S, " ", q);
break;
}
}
long q2 = q * q;
if (N % q2 == 0) {
long M = N / q2;
writeln(q, " ", M);
break;
}
}
}
}
E問題
DFSを用いて単純パスの個数を数え上げる。単純にDFSをしていくとTLEになるため単純パスの個数が$10^6$以上になった場合、処理を終了するようにする。
import std;
enum long L = 10 ^^ 6;
void main() {
long N, M;
readf("%d %d\n", N, M);
auto edges = new long[][](N);
foreach (_; 0 .. M) {
long u, v;
readf("%d %d\n", u, v);
--u, --v;
edges[u] ~= v, edges[v] ~= u;
}
auto seen = new bool[](N);
long f(long pos) {
long ret = 1;
seen[pos] = true;
foreach (edge; edges[pos]) {
if (seen[edge]) continue;
ret += f(edge);
if (ret >= L) break;
}
seen[pos] = false;
return ret;
}
long res = min(f(0), L);
res.writeln;
}