π§© AtCoder Beginner Contest 399
π§© AtCoder Beginner Contest 399
# Info & Summary
- Date:
2025-03-29
- Completion Status: A β / B β / C β / D β / E β / F β / G β
- Problem Type:
Problem | Type(s) | Data Structure / Algo | π₯ Key Insight / Mark |
---|---|---|---|
C | Graph | DFS/BFS/DSU | βπ οΈπ₯ |
D | Pairwise Pattern Check with Indexed Constraints | Simulation | β |
E | String/Graph | DSU | π§ πβ |
F | Algebraic Dynamic Programming | DP/Prefix Sum | π |
G | Tree | Matroid | π§ π |
Details
Emoji | Label | Meaning |
---|---|---|
β | Important | Problem is strategically useful or has reusable patterns |
π₯ | Must master | Classic problem or technique that appears often and is worth mastering |
β οΈ | Reattempt | You attempted but failed; worth reviewing and solving again |
π§ | New to me | Introduced a new idea, technique, or pattern you hadnβt seen before |
π οΈ | Template | You built or improved a code template from solving this problem |
π | Too hard now | Beyond current ability to understand/solve; revisit after more practice |
π A - Hamming Distance

#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
string s, t;
cin >> n >> s >> t;
int ans = 0;
for (int i = 0; i< n; i++)
{
ans += (s[i] != t[i]);
}
cout << ans << endl;
return 0;
}
π B - Ranking with Ties

#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<int> p(n);
for (int i = 0; i < n; i++)
{
cin >> p[i];
}
vector<int> idx(n);
vector<int> ans(n);
int r = 1;
iota(idx.begin(), idx.end(), 0);
sort(idx.begin(), idx.end(), [&](int i, int j)
{ return p[i] > p[j]; });
for (int i = 0; i < n; i++)
{
if (i == 0 || p[idx[i]] != p[idx[i - 1]])
{
r = i + 1;
}
ans[idx[i]] = r;
}
for (int i = 0; i < n; i++)
{
cout << ans[i] << endl;
}
return 0;
}
π C - Make it Forest

Suppose that the given graph consists of connected components . Then for each we can retain edges, which is the maximum possible. Together with , the maximum number of edges that can be retained is:
Therefore, the minimum number of edges required for the original graph to become a forest is:
Thus, the problem can be solved if we can find the number of connected components
Tips
The number of connected components can be counted fast by graph searching algorithns like DFS
or BFS
, or DSU
BFS
version
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n, vector<int>());
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<bool> vis(n, false);
queue<int> q;
int connected_count = 0;
for (int i = 0; i < n; i++)
{
if (vis[i])
{
continue;
}
connected_count++;
vis[i] = true;
q.push(i);
while (!q.empty())
{
int u = q.front();
q.pop();
for (auto v : adj[u])
{
if (vis[v])
{
continue;
}
vis[v] = true;
q.push(v);
}
}
}
cout << (m - (n - connected_count)) << endl;
return 0;
}
DFS
+ std::function
version
Slightly slower (due to internal indirection and type erasure)
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n, vector<int>());
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> vis(n, false);
function<void(int)> dfs = [&](int u)
{
vis[u] = true;
for (auto v : adj[u])
{
if (!vis[v])
{
dfs(v);
}
}
};
int connected_count = 0;
for (int i = 0; i < n; i++)
{
if (!vis[i])
{
connected_count++;
dfs(i);
}
}
cout << (m - (n - connected_count)) << endl;
return 0;
}
DFS
+ auto &self
version
Faster (no indirection or type erasure)
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n, vector<int>());
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> vis(n, false);
auto dfs = [&](auto &self, int u) -> void
{
vis[u] = true;
for (auto v : adj[u])
{
if (!vis[v])
{
self(self, v);
}
}
};
int connected_count = 0;
for (int i = 0; i < n; i++)
{
if (!vis[i])
{
connected_count++;
dfs(dfs, i);
}
}
cout << (m - (n - connected_count)) << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
struct DSU
{
vector<int> p, siz;
DSU(int n) : p(n), siz(n, 1)
{
iota(p.begin(), p.end(), 0);
}
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}
bool is_same(int x, int y)
{
return find(x) == find(y);
}
bool merge(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
{
return false;
}
siz[x] += siz[y];
p[y] = x;
return true;
}
int size(int x)
{
return siz[p[x]];
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
DSU dsu(n);
int connected_count = n;
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
u--;
v--;
if (dsu.merge(u, v))
{
connected_count--;
}
}
cout << (m - (n - connected_count)) << endl;
return 0;
}
π D - Switch Seats

Tips
How is placed in the sequence if it is satisfies the conditions in the problem statements?
In fact, it turn out that it is limited to:
or
Let , be the positions of in , and , be of
Let be the sequence sorted in ascending order, then and
If is applicable, then their occurrences should be adjacent too
Thus, we can count all the conforming pairs by checking the criteria above for all with ,
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
vector<int> A(2 * n);
for (int i = 0; i < 2 * n; i++)
{
cin >> A[i];
}
vector<vector<int>> pos(n + 1);
for (int i = 0; i < 2 * n; i++)
{
pos[A[i]].push_back(i);
}
set<pair<int, int>> s;
for (int i = 0; i + 1 < 2 * n; i++)
{
int a = A[i], b = A[i + 1];
if (pos[a][0] + 1 == pos[a][1] || pos[b][0] + 1 == pos[b][1])
{
continue;
}
vector<int> v = {pos[a][0], pos[a][1], pos[b][0], pos[b][1]};
sort(v.begin(), v.end());
if (v[0] + 1 == v[1] && v[2] + 1 == v[3])
{
s.emplace(minmax(a, b));
}
}
cout << s.size() << endl;
}
return 0;
}
π E - Replace

Warning
Hard Problems to Tackle Later
π F - Range Power Sum

Tips
Rephrase the problem statement as follows:
- There are boxes arranged from left to right, box contains balls
- How many ways are there to perform the following proceduce?
- Place two indistinguishable partitions between boxes in the sequence
- For each , choose one ball in a box between the two partitions, and put a sticker labeled . A single ball may have multiple stickers
:::
Let us inspect the ball from the left, and determine the position of partitions while putting stickers. We will perform the following DP
:
dp[i][j][k]
: the number of ways to inspect the first boxes, while inserting () partitions and putting () stickers
Tips
The transitions are as follows:
- : corresponds to placing a partition
- : advancing to the next box without putting a sticker
- , when : corresponds to putting a sticker on the ball in the box currently inspecting, where:
- the variable corresponds to the number of stickers to put
- the coefficient $$ to the number of ways to choose stickers out of stickers not attached yet
- the coefficient to the number of ways to choose balls to put sticker on for each of the chosen sticker
Note
Note that the third transition is only limited to because one can put a sticker on a ball only when it is in a box between the partitions
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
long long mod_pow(long long base, long long exp)
{
long long result = 1;
while (exp > 0)
{
if (exp % 2 == 1)
{
result = (result * base) % MOD;
}
base = (base * base) % MOD;
exp /= 2;
}
return result;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int N, K;
cin >> N >> K;
vector<long long> A(N);
for (int i = 0; i < N; i++)
{
cin >> A[i];
}
vector<long long> S(N + 1, 0);
for (int i = 1; i <= N; i++)
{
S[i] = (S[i - 1] + A[i - 1]) % MOD;
}
vector<vector<long long>> sum(K + 1, vector<long long>(N + 1, 0));
for (int m = 0; m <= K; m++)
{
sum[m][0] = 0;
for (int i = 1; i <= N; i++)
{
sum[m][i] = (sum[m][i - 1] + mod_pow(S[i - 1], m)) % MOD;
}
}
vector<vector<long long>> C(K + 1, vector<long long>(K + 1, 0));
C[0][0] = 1;
for (int n = 1; n <= K; n++)
{
C[n][0] = 1;
for (int k = 1; k <= n; k++)
{
C[n][k] = (C[n - 1][k - 1] + C[n - 1][k]) % MOD;
}
}
vector<long long> dp(K + 1, 0);
long long total = 0;
for (int r = 1; r <= N; r++)
{
for (int k = 0; k <= K; k++)
{
dp[k] = 0;
for (int m = 0; m <= k; m++)
{
long long term = (C[k][m] * mod_pow(S[r], k - m)) % MOD;
term = (term * mod_pow(-1, m)) % MOD;
term = (term * sum[m][r]) % MOD;
dp[k] = (dp[k] + term) % MOD;
}
}
total = (total + dp[K]) % MOD;
}
cout << (total + MOD) % MOD << endl;
return 0;
}
π G - Colorful Spanning Tree

Warning
Hard Problems to Tackle Later