はじめに
AtCoder Beginner Contest 293の復習記事です。
記事におけるScanner
クラスは、自作の入力クラスです。
コンテスト
https://atcoder.jp/contests/abc293
問題A
全ての奇数番目の文字と偶数番目の文字を入れ替えたものを出力します。
0-indexed
の場合、偶数番目と奇数番目であることに注意が必要です。
public static void Solve()
{
var S = Scanner.Scan<string>().ToCharArray();
var N = S.Length;
for (var i = 0; i < N / 2; i++)
{
(S[i * 2], S[i * 2 + 1]) = (S[i * 2 + 1], S[i * 2]);
}
var T = new string(S);
Console.WriteLine(T);
}
問題B
1<=i<=N
について、i
番目の人が呼ばれているかを配列で保持しながら、i
が呼ばれていなければA[i]
を呼ぶという操作を行います。
そして、呼ばれていないi
を列挙します。
public static void Solve()
{
var N = Scanner.Scan<int>();
var A = Scanner.ScanEnumerable<int>().ToArray();
var called = new bool[N];
for (var i = 0; i < N; i++)
{
if (!called[i]) called[A[i] - 1] = true;
}
var answers = Enumerable.Range(0, N).Where(x => !called[x]).ToArray();
Console.WriteLine(answers.Length);
Console.WriteLine(string.Join(" ", answers.Select(x => x + 1)));
}
問題C
(1,1)
から(H,W)
に移動するために必要な移動回数は、右移動H-1
回、下移動W-1
回の計H+W-2
回です。
右移動を0
、下移動を1
としたとき、2^(H+W-2)
通りの移動方法をbit全探索し、右H-1
回、下W-1
回になる移動方法を調べます。
全体の時間計算量はO(2^(H+W-2)(H+W-2))
となり、最大でも2^18*18==4718592
の計算で済み、十分高速です。
public static void Solve()
{
var (H, W) = Scanner.Scan<int, int>();
var A = new long[H][];
for (var i = 0; i < H; i++)
{
A[i] = Scanner.ScanEnumerable<long>().ToArray();
}
var K = H + W - 2;
var answer = 0;
for (var s = 0; s < 1 << K; s++)
{
var delta = new List<(int, int)>(K);
for (var i = 0; i < K; i++)
{
if ((s >> i & 1) == 0) delta.Add((1, 0));
else delta.Add((0, 1));
}
var ok = true;
var set = new HashSet<long>();
var (ch, cw) = (0, 0);
set.Add((A[0][0]));
foreach (var (dh, dw) in delta)
{
var (nh, nw) = (ch + dh, cw + dw);
if (nh < 0 || H <= nh || nw < 0 || W <= nw || set.Contains(A[nh][nw]))
{
ok = false;
break;
}
set.Add(A[nh][nw]);
ch = nh;
cw = nw;
}
if (ok)
{
answer++;
}
}
Console.WriteLine(answer);
}
問題D
頂点a
と頂点b
が連結であり、頂点a
と頂点c
が連結であるとき、頂点b
と頂点c
間に辺を追加すると閉路ができます。
これは、DisjointSetUnion
で高速に判定することができます。
問題について、各ロープには赤と青の区別された端があることから、赤と青の端をそれぞれ別の頂点としたとき、赤と青の頂点間に辺があると言えます。
あらかじめ各ロープの赤と青の頂点を連結し、各クエリごとにロープとその色に対応する頂点同士を閉路ができるかを判定しながら連結していくことで、環状になっているロープの組の数を数え上げることができます。
public static void Solve()
{
var (N, M) = Scanner.Scan<int, int>();
var dsu = new DisjointSetUnion(N * 2);
for (var i = 0; i < N; i++)
{
dsu.Merge(i * 2, i * 2 + 1);
}
var x = 0;
for (var i = 0; i < M; i++)
{
var query = Scanner.ScanEnumerable<string>().ToArray();
var (u, v) = ((int.Parse(query[0]) - 1) * 2, (int.Parse(query[2]) - 1) * 2);
var (uc, vc) = (query[1], query[2]);
if (uc == "B") u++;
if (vc == "B") v++;
if (dsu.IsSame(u, v)) x++;
dsu.Merge(u, v);
}
var y = dsu.GetGroups().Count() - x;
Console.WriteLine($"{x} {y}");
}