π§© Codeforces Round 1017 (Div. 4)
π§© Codeforces Round 1017 (Div. 4)
# Info & Summary
- Date:
2025-04-13
- Completion Status: A β / B β / C β / D β / E β / F β / G β / H β
- Problem Type:
Problem | Type(s) | Data Structure / Algo | π₯ Key Insight / Mark |
---|---|---|---|
E | Bitwise operation | Bitmask/Bitwise | π₯β |
F | Constructive | Constructive/Math | πβπ§ |
G | Implementation | Implementation/Math | πβ οΈ |
H | Number theory/Math | Number theory/Math/Binary search | πβ οΈ |
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 - Trippi Troppi

This can be solved by printing the zero-th
index of each string in sequence
#include <bits/stdc++.h>
using namespace std;
void solve()
{
string a, b, c;
cin >> a >> b >> c;
cout << a[0] << b[0] << c[0] << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
π B - Bobritto Bandito

Tips
The problem reduces to find and such that and
- If , we can choose and
- If , we can choose and
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n, m, l, r;
cin >> n >> m >> l >> r;
if (m <= -l)
{
cout << -m << " " << 0 << endl;
}
else
{
cout << l << " " << r - (n - m) << endl;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π C - Brr Brrr Patapim

#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int> nums(2 * n, -1);
set<int> s;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
int num;
cin >> num;
s.insert(num);
nums[i + j - 1] = num;
}
}
for (int i = 1; i <= 2 * n; i++)
{
if (s.find(i) == s.end())
{
nums[0] = i;
break;
}
}
for (int i = 0; i < 2 * n; i++)
{
cout << nums[i] << " \n"[i == 2 * n - 1];
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
Tips
Each velue from to must appear exactly once in , thus, we can simply find the value which has not appeared yet, and choose that as
- : the total value of if and appear exactly once
- : the current sun of
// Better version
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int> p(2 * n, -1);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
int num;
cin >> num;
p[i + j - 1] = num;
}
}
p[0] = 2 * n * (2 * n + 1) / 2 - accumulate(p.begin() + 1, p.end(), 0);
for (int i = 0; i < 2 * n; i++)
{
cout << p[i] << " \n"[i == 2 * n - 1];
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π D - Tung Tung Sahur

First, suppose that there is only in both and . Then it is clear that the answer is yes
if and only if:
My code
#include <bits/stdc++.h>
using namespace std;
void solve()
{
string p, s;
cin >> p >> s;
p += ".";
s += ".";
// cout << p << " " << s << endl;
int l1 = 0, l2 = 0;
int r1 = 0, r2 = 0;
while (r1 < p.size() && r2 < s.size())
{
while (r1 < p.size() && p[r1] == p[l1])
{
r1++;
}
while (r2 < s.size() && s[r2] == s[l2])
{
r2++;
}
if (r1 >= p.size())
{
r1--;
}
if (r2 >= s.size())
{
r2--;
}
// cout << l1 << " " << r1 << endl;
// cout << l2 << " " << r2 << endl;
if (p[l1] != s[l2])
{
cout << "NO" << endl;
return;
}
if (2 * (r1 - l1) < (r2 - l2))
{
cout << "NO" << endl;
return;
}
if ((r1 - l1) > (r2 - l2))
{
cout << "NO" << endl;
return;
}
l1 = r1;
l2 = r2;
r1 = l1 + 1;
r2 = l2 + 1;
}
// cout << l1 << " " << l2 << endl;
if (l1 < p.size() - 1 || l2 < s.size() - 1)
{
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
// Better version
#include <bits/stdc++.h>
using namespace std;
void solve()
{
string p, s;
cin >> p >> s;
int i = 0;
for (int l = 0, r = 0; l < p.size(); l = r)
{
while (r < p.size() && p[l] == p[r])
{
r++;
}
if (i == s.size() || s[i] != p[l])
{
cout << "NO\n";
return;
}
int j = i;
while (j < s.size() && s[i] == s[j])
{
j++;
}
if (j - i < r - l || j - i > 2 * (r - l))
{
cout << "NO\n";
return;
}
i = j;
}
if (i == s.size())
{
cout << "YES\n";
}
else
{
cout << "NO\n";
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
Tips
We can also partition into blocks
(where we define a block to be a maximal group of contiguous identical characters)
Then the answer is YES
if and only if is the concatenation of extensions of block in
#include <bits/stdc++.h>
using namespace std;
void solve()
{
string a, b;
cin >> a >> b;
int n = a.size(), m = b.size();
if (m < n || m > 2 * n || a[0] != b[0])
{
cout << "NO\n";
return;
}
auto calc = [&](string s)
{
vector<int> group;
int cnt = 1;
for (int i = 1; i < s.size(); i++)
{
if (s[i] != s[i - 1])
{
group.push_back(cnt);
cnt = 1;
}
else
{
cnt++;
}
}
group.push_back(cnt);
return group;
};
auto aa = calc(a);
auto bb = calc(b);
if (aa.size() != bb.size())
{
cout << "NO\n";
return;
}
n = aa.size();
for (int i = 0; i < n; i++)
{
if (aa[i] > bb[i] || aa[i] * 2 < bb[i])
{
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π E - Boneca Ambalabu

Tips
Consider each bit independently:
- For , let denote the number of elements of which have the -th bit set(where bits are indexed from the least significant bit being the -th bit)
Now, suppose we choose a particular , then we can find the sum as follows:
- For a given bit position , the number of elements such that has the -th bit set will be
- : if the -th bit of is not set, so we can add to the answer
- : if the -th bit of is set, so we can add to the answer
// My version
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int> cnt(31, 0);
vector<long long> nums(n);
for (int i = 0; i < n; i++)
{
long long num;
cin >> num;
nums[i] = num;
int idx = 0;
while (num > 0)
{
cnt[idx] += num % 2;
idx++;
num /= 2;
}
}
// for (int i = 0; i < 31; i++)
// {
// cout << cnt[i] << " \n"[i == 30];
// }
long long ans = 0;
for (int i = 0; i < n; i++)
{
vector<int> a(31, 0);
int idx = 0;
while (nums[i] > 0)
{
a[idx] += nums[i] % 2;
idx++;
nums[i] /= 2;
}
long long temp = 0;
for (int j = 0; j < 31; j++)
{
if (a[j] == 1)
{
temp += (n - cnt[j]) * 1ll * pow(2, j);
}
else
{
temp += cnt[j] * 1ll * pow(2, j);
}
}
if (temp > ans)
{
ans = temp;
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
How to calculate quickly
for (int j = 0; j < 30; j++)
{
cnt[j] += a[i] >> j & 1;
}
// Better version
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
vector<int> cnt(30, 0);
for (int i = 0; i < n; i++)
{
cin >> a[i];
for (int j = 0; j < 30; j++)
{
cnt[j] += a[i] >> j & 1;
}
}
long long ans = 0;
for (int i = 0; i < n; i++)
{
long long cur = 0;
for (int j = 0; j < 30; j++)
{
cur += (a[i] >> j & 1 ? n - cnt[j] : cnt[j]) * (1ll << j);
}
ans = max(ans, cur);
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π F - Trulimero Trulicina

First, we can output the numbers in the natural reading order. For example , and , we would output the following:
1 | 2 | 3 | 4 |
5 | 6 | 1 | 2 |
3 | 4 | 5 | 6 |
Warning
When is a multiple of , for example, , and , then we get:
1 | 2 | 3 | 1 | 2 | 3 |
1 | 2 | 3 | 1 | 2 | 3 |
1 | 2 | 3 | 1 | 2 | 3 |
1 | 2 | 3 | 1 | 2 | 3 |
Tips
We can fix this by Cyclically shifting
every other row as follows:
1 | 2 | 3 | 1 | 2 | 3 |
2 | 3 | 1 | 2 | 3 | 1 |
3 | 1 | 2 | 3 | 1 | 2 |
1 | 2 | 3 | 1 | 2 | 3 |
Warning
I don't fully understand the code below
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n, m, k;
cin >> n >> m >> k;
// The GCD of (n, k) gives us a natural way to divide the rows into groups
int a = gcd(n, k);
int b = k / a;
assert(m % b == 0);
if (a > 1 && b > 1)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cout << i % a * b + j % b + 1 << " \n"[j == m - 1];
}
}
}
else if (a == 1)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cout << i % a * b + (i + j) % b + 1 << " \n"[j == m - 1];
}
}
}
else
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cout << (i + j) % a * b + (i + j) % b + 1 << " \n"[j == m - 1];
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π G - Chimpanzini Bananini

Tips
Note that reversing an array then pushing an element to the back is similar to simply pushing an element to the front.
Thus, we would to use the deque
data structure
To keep track of score, we need to maintain the values:
- the current score
- the score of the array if it were backwards
- the size of the array
- the sum of the array
- Suppose operation
1
is performed, this is equivalent to pop the back element of the array and push it in the front.- When you pop the back element of the array,
score
decreases by - Then when you push it to the front,
score
increases by because is pushed to the first spot and every element form to moves forward one spot - Notice that
rscore
changes in the reverse way, when you pop the first element of the array,rscore
decreases by , and when you push it to the back,rscore
increases by size
andsum
remain unchanged
- When you pop the back element of the array,
- Suppose operation
2
is performed. Then we swapscore
andrscore
, and we also want toreverse
the array- However, it is costly to entirely reverse the deque
- Instead, we will set a flag to indicate that the array has been reversed. If the flag is set, we simply switch the front and back ends whenever we access or modify the deque while performing operations
1
or3
- Suppose operation
3
is performed- then we see that
size
increases by1
andsum
increases byk
- Then
score
increases by - ans
rscore
increases bysum
, by identical reasoning to that in operation1
- then we see that
Tips
We don't actually to maintain rscore
, we can obtain it with the expression:
This is because since -th term is counted times in score
and times in rscore
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int q;
cin >> q;
deque<int> a;
long long sum = 0;
long long ans = 0;
bool rev = false;
for (int i = 0; i < q; i++)
{
int s;
cin >> s;
if (s == 1)
{
if (!rev)
{
int x = a.back();
ans += sum;
ans -= (long long)(a.size()) * x;
a.pop_back();
a.push_front(x);
}
else
{
int x = a.front();
ans -= sum;
ans += (long long)(a.size()) * x;
a.pop_front();
a.push_back(x);
}
}
else if (s == 2)
{
rev ^= 1;
}
else
{
int k;
cin >> k;
if (!rev)
{
ans += (long long)(a.size()) * k;
sum += k;
a.push_back(k);
}
else
{
ans += sum;
sum += k;
a.push_front(k);
}
}
if (!rev)
{
cout << ans + sum << endl;
}
else
{
cout << sum * (long long)(a.size()) - ans << endl;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
π H - La Vaca Saturno Saturnita

Tips
a[i]
must be a divisor of in order for to change
Tips
For each divisor of , can only change once, because after the first iteration with , is no longer divisible by
Thus, the number of times changes is bounded by the number of divisors of , denoted by
And can be found in time by checking if divides for every form to , and if so, and are both divisors of
We make the following observations:
- the value of only changes if is a divisor of
- if there exists with , then the value of will not change at (because after the iteration , we have that is no longer divisible by )
Now, we would like to find the first time each divisor of appears in . This can be done by storing the array in a map, where each value is mapped to a vector of indices where that values appears. Then for a given divisor of , we can use lower_bound()
to find the smallest index at or after where this divisor appears, and we can check if it is no greater than
Now, we have a list of indices at which might change:
- suppose that changes to at index , and then changes to at index
- Then we know that we have a value of from to , so we can add to
ans
- We can do this for all changes
Tips
We can use a vertor(of vectors) instead of a map, since is reasonably samll
Note that instantiating a vector of this size in every test case is too slow, so we may have to instantiate it globally and clear the vertors that were used after each test case
Tips
Another optimization is to preprocess divisors instead of computing them on the spot. We can compute and store the divisors of all integers in time in a vector of vectors where contains all divisors of as follows:
- For all , we push into for all multiples of of
This allow us to remove the term in the runtime of each query
#include <bits/stdc++.h>
using namespace std;
constexpr int V = 1e5;
constexpr int inf = 1e9;
int pos[V + 1];
vector<int> factors[V + 1];
void solve()
{
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
vector<int> k(q), r(q);
vector<vector<int>> qry(n);
for (int i = 0; i < q; i++)
{
int l;
cin >> k[i] >> l >> r[i];
l--;
qry[l].push_back(i);
}
vector<long long> ans(q);
vector<int> nxt(n, inf);
for (int i = n - 1; i >= 0; i--)
{
nxt[i] = pos[a[i]];
pos[a[i]] = i;
}
for (int l = 0; l < n; l++)
{
for (auto i : qry[l])
{
while (k[i] % a[l] == 0)
{
k[i] /= a[l];
}
int t = inf;
for (auto d : factors[k[i]])
{
t = min(t, pos[d]);
}
if (t >= r[i])
{
ans[i] += 1ll * (r[i] - l) * k[i];
}
else
{
ans[i] += 1ll * (t - l) * k[i];
assert(t > l);
qry[t].push_back(i);
}
}
pos[a[l]] = nxt[l];
}
for (int i = 0; i < q; i++)
{
cout << ans[i] << endl;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
fill(pos, pos + V + 1, inf);
for (int i = 2; i <= V; i++)
{
for (int j = i; j <= V; j += i)
{
factors[j].push_back(i);
}
}
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}