はじめに
AtCoder Beginner Contest 292の復習記事です。
記事におけるScanner
クラスは、自作の入力クラスです。
コンテスト
https://atcoder.jp/contests/abc292
問題A
string.ToUpper
で文字列をすべて大文字にすることができます。
public static void Solve()
{
var S = Scanner.Scan<string>();
var answer = S.ToUpper();
Console.WriteLine(answer);
}
問題B
Y[x]
をその時点のx
のイエローカードの数とし、レッドカードはイエローカード2枚分であるとします。
そして、各イベントについて次のような処理を行います。
- イベントが
1
の場合、Y[x]+=1
- イベントが
2
の場合、Y[x]+=2
- イベントが
3
の場合、Y[x]>=2
ならば退場処分を受けている。
public static void Solve()
{
var (N, Q) = Scanner.Scan<int, int>();
var C = new int[N];
while (Q-- > 0)
{
var (e, x) = Scanner.Scan<int, int>();
x--;
if (e == 3)
{
var answer = C[x] >= 2 ? "Yes" : "No";
Console.WriteLine(answer);
}
else
{
C[x] += e;
}
}
}
問題C
また、AB
の値がX
の場合、A*B==X
となるA
とB
の組み合わせの個数は、X
の約数の個数と一致します。
これは、時間計算量O(Sqrt(X))
で求めることができます。
また、C(n)
をn
の約数の個数としたとき、X+Y=N
となる組み合わせはC(X)*C(Y)
個になります。
そして、AB
の値をX
としたとき、CD
の値はY=N-X
で求められるので、AB
を固定することで全体計算量O(NSqrt(N))
で求めることができます。
public static void Solve()
{
var N = Scanner.Scan<long>();
var dict = new Dictionary<long, long>();
long F(long n)
{
if (dict.ContainsKey(n)) return dict[n];
long result = 0;
for (var a = 1L; a * a <= n; a++)
{
if (n % a != 0) continue;
var b = n / a;
if (a == b) result++;
else result += 2;
}
return dict[n] = result;
}
long answer = 0;
for (var ab = 1; ab < N; ab++)
{
var cd = N - ab;
answer += F(ab) * F(cd);
}
Console.WriteLine(answer);
}
問題D
DisjointSetUnion
を使って、各連結成分の代表する頂点を求められるようにしておきます。
そして、連結成分の頂点の個数と辺の個数をそれぞれ各連結成分の代表にまとめ、それらを比較することで、各連結成分の頂点の個数を辺の個数が一致するかを求めることができます。
public static void Solve()
{
var (N, M) = Scanner.Scan<int, int>();
var G = new List<int>[N].Select(x => new List<int>()).ToArray();
var E = new (int U, int V)[M];
var dsu = new DisjointSetUnion(N);
for (var i = 0; i < M; i++)
{
var (u, v) = Scanner.Scan<int, int>();
u--; v--;
E[i] = (u, v);
dsu.Merge(u, v);
}
var vc = new int[N];
for (var u = 0; u < N; u++)
{
vc[dsu.LeaderOf(u)]++;
}
var ec = new int[N];
foreach (var (u, _) in E)
{
ec[dsu.LeaderOf(u)]++;
}
var answer = true;
for (var u = 0; u < N; u++)
{
answer &= vc[u] == ec[u];
}
Console.WriteLine(answer ? "Yes" : "No");
}
問題E
ある頂点を始点s
としたとき、その頂点からたどり着くことができる頂点t
に対して、s->t
の辺を張ることができます。
このことから、各頂点を始点としたときに、たどり着くことができる頂点を幅優先探索などで探索することで、張ることができる辺の数を求めることができます。
public static void Solve()
{
var (N, M) = Scanner.Scan<int, int>();
var G = new List<int>[N].Select(x => new List<int>()).ToArray();
for (var i = 0; i < M; i++)
{
var (u, v) = Scanner.Scan<int, int>();
u--; v--;
G[u].Add(v);
}
var used = new bool[N];
var queue = new Queue<int>();
var answer = -M;
for (var i = 0; i < N; i++)
{
Array.Fill(used, false);
used[i] = true;
queue.Enqueue(i);
while (queue.Count > 0)
{
var u = queue.Dequeue();
foreach (var v in G[u])
{
if (used[v]) continue;
used[v] = true;
answer++;
queue.Enqueue(v);
}
}
}
Console.WriteLine(answer);
}