Thank you for participating in our round! We hope you enjoyed the problems.
2234A - Euclid, Sequence and Two Numbers
Idea: FairyWinx
Note that $$$(a_i \bmod a_{i + 1}) \lt a_{i + 1}$$$.
$$$a_{i + 2} = (a_i \bmod a_{i + 1}) \lt a_{i + 1}$$$ and $$$a_2 \le a_1$$$ mean that the sequence $$$a$$$ must be non-increasing. On the other hand, there is only one permutation of the sequence $$$b$$$ that can be non-increasing.
t = int(input())
for tt in range(t):
n = int(input())
a = list(map(int, input().split()))
a.sort()
a = a[::-1]
valid = True
for i in range(2, n):
if a[i] != a[i - 2] % a[i - 1]:
print(-1)
valid = False
break
if valid:
print(a[0], a[1])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> b[i];
}
sort(b.rbegin(), b.rend());
bool ok = true;
for (int i = 0; i < n - 2; i++) {
if (b[i + 2] != b[i] % b[i + 1]) {
ok = false;
break;
}
}
if (ok) {
cout << b[0] << " " << b[1] << "\n";
} else {
cout << -1 << "\n";
}
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
2234B - Palindrome, Twelve and Two Terms
Idea: Fakewave
tt = int(input())
for tc in range(tt):
n = int(input())
if n == 10:
print(-1)
elif n % 12 == 10:
print(22, n - 22)
else:
print(n % 12, n - (n % 12))
#include <bits/stdc++.h>
using namespace std;
void solve() {
long long n;
cin >> n;
if (n == 10) {
cout << "-1\n";
} else if (n % 12 == 10) {
cout << "22 " << n - 22 << "\n";
} else {
cout << n % 12 << " " << n - (n % 12) << "\n";
}
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234C - Vessels, Heights and Two Versions (Easy Version)
Idea: yanb0
Intuitively, the farther a vessel from the empty one, the more water there can be. Fix the empty vessel and try to explicitly find the formula for the maximum possible water height in each of the other vessels.
t = int(input())
for tc in range(t):
n = int(input())
h = list(map(int, input().split()))
ans = []
for s in range(n):
w1 = [0] * n
w2 = [0] * n
for i in range(1, n):
w1[(s + i) % n] = max(w1[(s + i - 1) % n], h[(s + i - 1) % n])
for i in range(1, n):
w2[(s + n - i) % n] = max(w2[(s + n - i + 1) % n], h[(s + n - i) % n])
w = [min(w1[i], w2[i]) for i in range(n)]
ans.append(sum(w))
print(*ans)
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> h(n);
for (int i = 0; i < n; i++) cin >> h[i];
for (int s = 0; s < n; s++) {
vector<int> w1(n), w2(n), w(n);
for (int i = 1; i < n; i++) {
w1[(s + i) % n] = max(w1[(s + i - 1) % n], h[(s + i - 1) % n]);
}
for (int i = 1; i < n; i++) {
w2[(s + n - i) % n] = max(w2[(s + n - i + 1) % n], h[(s + n - i) % n]);
}
for (int i = 0; i < n; i++) {
w[i] = min(w1[i], w2[i]);
}
cout << accumulate(w.begin(), w.end(), 0ll) << " ";
}
cout << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234D - XOR, Expression and Two Binary Numbers
Idea: Fakewave
Let $$$A = a_1$$$, $$$B = a_{2^k + 1}$$$, $$$C = A \oplus B$$$.
Try to write out some values of $$$a$$$, for example, for $$$k = 3$$$.
$$$A \oplus B = C$$$, $$$B \oplus C = A$$$, $$$C \oplus A = B$$$.
Let's see how $$$a$$$ changes step by step:
a = [A, ?, ?, ?, ?, ?, ?, ?, B];a = [A, ?, ?, ?, C, ?, ?, ?, B];a = [A, ?, B, ?, C, ?, A, ?, B];a = [A, C, B, A, C, B, A, C, B].
Try to notice and prove a pattern.
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n), b(n);
char g;
for (int i = 0; i < n; ++i) {
cin >> g;
a[i] = g - '0';
}
for (int i = 0; i < n; ++i) {
cin >> g;
b[i] = g - '0';
}
vector<int> c(4);
for (int i = 0; i < n; ++i) {
int x = 2 * a[i] + b[i];
c[x]++;
}
if (k % 2) {
long long p = 0, q = 0, r = 0;
p = c[0] + c[1];
q = c[0] + c[3];
r = c[0] + c[2];
cout << (((1ll << k) + 1) / 3) * (p * (n-p) + q * (n - q) + r * (n - r)) << "\n";
} else {
long long p = 0, q = 0, r = 0;
p = c[0] + c[1];
q = c[0] + c[2];
r = c[0] + c[3];
cout << (((1ll << k) + 1) / 3) * (p * (n-p) + q * (n - q) + r * (n - r)) + p * (n - p) + q * (n - q) << "\n";
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
}
There is a rather straightforward solution that works in $$$\mathcal{O}(nk \log k)$$$.
Read the first two hints for Solution 1. Prove that each $$$a_i \in [A, B, C]$$$.
For every pair of bits $$$(x, y)$$$, count the number of positions $$$h$$$ for which the $$$h$$$-th bits of $$$a_1$$$ and $$$a_{2^k+1}$$$ are $$$x$$$ and $$$y$$$ respectively. Continue recursively.
Use caching.
Let the answer for the problem where $$$a_1 = A$$$, $$$a_{2^k+1} = B$$$ and $$$k = l$$$ be $$$F(l, A, B)$$$.
Then, as we write a value $$$A \oplus B$$$ in the middle of the array on the first step and then continue recursively, like for $$$k = l - 1$$$, $$$F(l, A, B) = F(l - 1, A, A \oplus B) + F(l - 1, A \oplus B, B) - [\text{product of the } 0 \text{ and } 1 \text{ bit counts for } A \oplus B]$$$.
Referring to the proof in Solution 1 that each $$$a_i \in [A, B, C]$$$, for each $$$l$$$ the function's value must be calculated for different pairs of $$$(A, B)$$$ at most $$$6$$$ times, thus, if we use a map for caching the answer of $$$F$$$, we will make not more than $$$6k$$$ calls to it, which makes the time complexity $$$\mathcal{O}(n + k \log k)$$$.
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, k, a, b, c;
string p, q, r;
int get(string &s) {
int a = 0, b = 0;
for (char c : s)
if (c == '0') a++;
else b++;
return a * b;
}
map<array<int, 4>, int> mp;
int solve(int l, int a, int b, int c) {
array<int, 4> t = {l, a, b, c};
if (mp.find(t) == mp.end()) {
if (l == 0) mp[t] = a + b;
else mp[t] = solve(l - 1, c, a, b) + solve(l - 1, b, c, a) - c;
}
return mp[t];
}
void test_case() {
cin >> n >> k >> p >> q;
r.clear();
for (int i = 0; i < n; i++)
r.push_back('0' + ((p[i] - '0') ^ (q[i] - '0')));
a = get(p);
b = get(q);
c = get(r);
mp.clear();
cout << solve(k, a, b, c) << '\n';
}
int32_t main() {
ios::sync_with_stdio(0); cin.tie(0);
int t; cin >> t;
while (t--) test_case();
}
2234E - Vlad, Misha and Two Arrays
Idea: Fakewave
Looking only at the array $$$a$$$, how to deduce the index $$$i$$$ for which $$$p_i = 1$$$, i.e. the minimum of $$$p$$$?
Try to come up with a recursive solution that works in $$$\mathcal{O}(n^2)$$$.
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Mod = 1e9 + 7, Max = 5e5 + 10;
vector<int> fact(Max), ifact(Max);
int binpow(int b, int p) {
if (p == 0) return 1;
if (p % 2 == 0) return binpow((b * b) % Mod, p / 2);
return (binpow(b, p - 1) * b) % Mod;
}
int C(int n, int k) {
return (fact[n] * ((ifact[k] * ifact[n - k]) % Mod)) % Mod;
}
int rec(int l, int r, vector<int> &b) {
if (r < l) {
return 1;
}
if (l == r) {
return (b[l] == 1 ? 1 : 0);
}
for (int d = 0; d < r - l + 1; d++) {
int i = d + l;
if ((i - l + 1) * (r - i + 1) == b[i]) {
return (((rec(l, i - 1, b) * rec(i + 1, r, b)) % Mod) * C(r - l, i - l)) % Mod;
}
i = r - d;
if ((i - l + 1) * (r - i + 1) == b[i]) {
return (((rec(l, i - 1, b) * rec(i + 1, r, b)) % Mod) * C(r - l, i - l)) % Mod;
}
}
return 0;
}
void solve() {
int n;
cin >> n;
vector<int> b(n);
for (int i = 0; i < n; i++) cin >> b[i];
int s = accumulate(b.begin(), b.end(), 0ll);
if (s != n * (n + 1) / 2) {
cout << "0\n";
return;
}
cout << rec(0, n - 1, b) << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
fact[0] = 1;
for (int i = 1; i < Max; i++) {
fact[i] = (fact[i - 1] * i) % Mod;
}
for (int i = 0; i < Max; i++) {
ifact[i] = binpow(fact[i], Mod - 2);
}
int t;
cin >> t;
while (t--) {
solve();
}
}
Will be added soon.
2234F - Vessels, Heights and Two Versions (Hard Version)
Idea: yanb0
Read the solution of C. Try to calculate the answer when the empty vessel is vessel $$$1$$$, then somehow change it quickly when moving the empty vessel to be vessel $$$2$$$, $$$3$$$, etc.
Consider the highest vessel connection.
You do not need any advanced data structures. Use a stack.
INF = float("inf")
tt = int(input())
for tc in range(tt):
n = int(input())
h = list(map(int, input().split()))
t = 0
for i in range(n):
if h[i] > h[t]:
t = i
ls = [0 for i in range(n)]
rs = [0 for i in range(n)]
sm = [(INF, 0)]
for ti in range(1, n):
i = (ti + t) % n
s = ls[i] + h[i]
c = 1
while sm[-1][0] <= h[i]:
s += sm[-1][1] * (h[i] - sm[-1][0])
c += sm[-1][1]
sm.pop()
sm.append((h[i], c))
ls[(i + 1) % n] = s
sm = [(INF, 0)]
for ti in range(1, n):
i = (t + n - ti) % n
s = rs[(i + 1) % n] + h[i]
c = 1
while sm[-1][0] <= h[i]:
s += sm[-1][1] * (h[i] - sm[-1][0])
c += sm[-1][1]
sm.pop()
sm.append((h[i], c))
rs[i] = s
ans = [ls[i] + rs[i] for i in range(n)]
print(*ans)
#include <bits/stdc++.h>
#define int long long
using namespace std;
using pii = pair<int, int>;
void solve() {
int n;
cin >> n;
vector<int> h(n);
for (int i = 0; i < n; i++) cin >> h[i];
int t = max_element(h.begin(), h.end()) - h.begin();
vector<int> ls(n), rs(n);
stack<pii> sm;
sm.push({1e18, 0});
for (int ti = 1; ti < n; ti++) {
int i = (ti + t) % n;
int s = ls[i] + h[i], c = 1;
while (sm.top().first <= h[i]) {
s += sm.top().second * (h[i] - sm.top().first);
c += sm.top().second;
sm.pop();
}
sm.push({h[i], c});
ls[(i + 1) % n] = s;
}
sm = stack<pii>();
sm.push({1e18, 0});
for (int ti = 1; ti < n; ti++) {
int i = (t + n - ti) % n;
int s = rs[(i + 1) % n] + h[i], c = 1;
while (sm.top().first <= h[i]) {
s += sm.top().second * (h[i] - sm.top().first);
c += sm.top().second;
sm.pop();
}
sm.push({h[i], c});
rs[i] = s;
}
for (int i = 0; i < n; i++) {
cout << ls[i] + rs[i] << " ";
}
cout << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234G - Stripe, Token and Two Players
Idea: yanb0
Think of a $$$\mathcal{O}(n^3)$$$ solution.
DP. For each pair $$$(cell, strength)$$$ you can find whether the player that makes the move from the position wins if the game is played optimally.
There are not many losing positions in this game. Why?
The number of losing positions $$$(i, k)$$$ for a fixed $$$k$$$ with $$$i \le n$$$ is no more than $$$\big\lceil \frac{n}{k + 1} \big\rceil$$$, since if position $$$(i, k)$$$ is a losing position, then all positions $$$(i + 1, k), (i + 2, k), \ldots, (i + k, k)$$$ must be winning if they exist. Then the total number of losing positions $$$(i, k)$$$ with $$$i \le n$$$ is no more than $$$\mathcal{O}(n \log n)$$$.
Try to iterate over cell number $$$i$$$ from $$$n$$$ to $$$1$$$ and explicitly find all losing positions.
You need to think of a data structure to optimize the solution to $$$\mathcal{O}(n \log^2 n)$$$.
We'll denote the state of the game where a chip is on cell $$$i$$$ and has strength $$$k$$$ before using bonuses as $$$(i, k)$$$. We need to determine whether position $$$(1, 1)$$$ is a winning position.
Note that the number of losing positions $$$(i, k)$$$ for a fixed $$$k$$$ with $$$i \le n$$$ is no more than $$$\big\lceil \frac{n}{k + 1} \big\rceil$$$, since if position $$$(i, k)$$$ is a losing position, then all positions $$$(i + 1, k), (i + 2, k), \ldots, (i + k, k)$$$ must be winning if they exist. Then the total number of losing positions $$$(i, k)$$$ with $$$i \le n$$$ is no more than $$$\mathcal{O}(n \log n)$$$.
We will iterate over cell number $$$i$$$ from $$$n$$$ to $$$1$$$ and explicitly find all losing positions.
Let's maintain a set $$$S$$$ containing all $$$k$$$ for which there is no losing position $$$(j, k)$$$, where $$$i \lt j \le i + k$$$. This can be done using a priority queue, adding the event "no more losing positions with strength $$$k$$$ on the interval $$$[i + 1, i + k]$$$" to position $$$i - k - 1$$$ when position $$$(i, k)$$$ is losing. (Also, this event addition is done initially for positions $$$(n + 1, 0), (n + 1, 1), \ldots, (n + 1, n)$$$, since they are all losing positions, and the strength can be limited to being not more than $$$n$$$ without changing the game.) The total number of such events added to the queue will be no more than $$$\mathcal{O}(n \log n)$$$ (based on the number of losing positions), which means there will be no more than $$$\mathcal{O}(n \log n)$$$ queries to the queue, since in each cell we will only read and delete events related to it. Note that only when we read such an event do we add one element to $$$S$$$, and the number of additions to $$$S$$$ is also no more than $$$\mathcal{O}(n \log n)$$$.
We will find losing positions for cell $$$i$$$ as follows: a position $$$(i, k)$$$ is a losing position if and only if, for all $$$0 \le d \le a_i$$$, there are no losing positions among positions $$$(j, k + d)$$$ for $$$j \in [i + 1, i + k + d]$$$, i.e., if and only if $$$S$$$ contains all $$$k, k + 1, \ldots, k + a_i$$$. To quickly find such $$$k$$$, we use the following data structure (we will call it a block container):
There is a set $$$s$$$ of natural numbers, initially empty. All numbers in $$$s$$$ will be on the interval $$$[1, n]$$$, and all operations are guaranteed to be valid (for example, a number already contained in $$$s$$$ will not be asked to be added there). A block container can:
- add a number $$$x$$$ to $$$s$$$ in $$$\mathcal{O}(\log n)$$$;
- find the length of the longest segment of consecutive numbers in $$$s$$$ in $$$\mathcal{O}(1)$$$;
- find any longest segment of consecutive numbers in $$$s$$$ and remove its smallest element from $$$s$$$ in $$$\mathcal{O}(\log n)$$$.
If written in C++, a container of blocks can be implemented, for example, using two std::sets storing the same set of blocks (segments) of consecutive numbers in $$$s$$$ (numbers adjacent to the edges of a block must not be in $$$s$$$; for example, for $$$s = {1, 5, 3, 2, 6}$$$, the set of blocks will be $$${[1, 3], [5, 6]}$$$). The first std::set will sort the blocks by their left boundary, and the second by length.
- When adding a number $$$x$$$ to $$$s$$$, the container adds a new block $$$[x, x]$$$, and then, using the first
std::set, it finds whether some segments need to be merged with the new one (i.e., whether there are two blocks whose boundaries are adjacent numbers), and, if so, merges them. The container merges segments by removing the old pair of segments from bothstd::sets and adding the merged segment back to eachstd::set. - To find the longest segment, the container simply accesses the second
std::set. - To remove the left element of the segment found above, the container removes the segment from both
std::sets, increments its left bound by $$$1$$$, and, if the segment is still non-empty, adds it back to eachstd::set.
The code can be written similarly in other languages, if there's a built-in set that supports custom sorting and finding the smallest element. Alternatively, a segment tree can be written on a boolean array of length $$$n$$$, reflecting the presence/absence ($$$1$$$/$$$0$$$) of numbers in $$$s$$$, storing at a node the boundaries of the longest subsegment of the segment of the node consisting of consecutive 1s.
Now, let's show how to quickly find the required $$$k$$$. We'll store $$$S$$$ in a container of blocks. If the length of some block in $$$S$$$ is greater than $$$a_i$$$ and equals $$$l + a_i$$$, the $$$l$$$ smallest elements of this block will be losing strengths for cell $$$i$$$ — this determines all losing strengths for cell $$$i$$$ and only them. Then let's simply remove the smallest element of the longest block while its length is greater than $$$a_i$$$ — obviously, and all the removed numbers will be all the losing strengths for cell $$$i$$$. The number of requests to the block container for finding and removing when considering cell $$$i$$$ is then $$$1$$$ more than the number of losing positions $$$(i, k)$$$, that is, in total over all cells, no more than $$$\mathcal{O}(n \log n)$$$. The number of requests to the block container for adding, as we showed earlier, is also not more than $$$\mathcal{O}(n \log n)$$$.
That is, for each cell, we first process the events coming from the priority queue, load the new elements into the $$$S$$$ block container, then find the losing positions, and finally add new events for the next cells to the priority queue. This means we get every losing position in the process; all that remains is to check whether position $$$(1, 1)$$$ is a losing position.
This results in $$$\mathcal{O}(n \log^2 n)$$$ for requests to the priority queue, $$$\mathcal{O}(n \log^2 n)$$$ for requests to the block container, and a total asymptotic complexity of $$$\mathcal{O}(n \log^2 n)$$$.








A fun fact that CCO '26 P2 is similar to problem E :) In that question you are supposed to output a construction instead of finding the total possible permutations.
an O(n) solution for E: 377668463
The idea is that you can find the nearest smaller element to the left and right of each position using a monotonic stack. These determine the cartesian tree corresponding to all valid permutations
Thank you! Our testers also pointed out that there is an $$$\mathcal{O}(n)$$$ solution (that is one of the reasons for the decision of making the problem have the index E), and we are planning to add it to the tutorial soon.
I did the same, although implementation ended up easier maintaining only two arrays l and r of nearest bigger element to the left and to the right (without the stacks): 377671821
I took a different approach to E, filling in numbers top-down rather than from left/right ends. Idea is to start with a set of intervals $$$\left\lbrace[i,i] : a[i]=1\right\rbrace$$$ (which must be the local maxima) and trivial intervals between adjacent elements. Repeatedly consider the endpoints of the intervals (only needing to reconsider a point when it's the endpoint of a new interval). For each interval maintain the total number of possible orderings, and when we join two intervals together, the number of orderings of the new one is $$$\text{left_orderings}*\text{right_orderings}*\binom{\text{left_size}+\text{right_size}}{\text{left_size}}$$$; continue until we can't proceed or everything has been joined into one interval. I did this with DSU for $$$O(n \alpha(n))$$$ but it can straightforwardly be done in $$$O(n)$$$ using linked lists.
there is some writing mistake in E's solution :
let's check this condition for the indices [l,r] in the following order: l,r,l+1,r−1,r+2,r−2,… . We will prove that with this optimization, the algorithm will run in O(n^2) time.should be
let's check this condition for the indices [l,r] in the following order: l,r,l+1,r−1,l+2,r−2,… . We will prove that with this optimization, the algorithm will run in O(nlogn) time.Thanks, fixed now
what about in E->Solution->4.
does the combinatoric notation works as C[top:choose][bottom:total]
in my experience it's the opposite
Huh, today I learned from Wikipedia that "from $$$n$$$ choose $$$k$$$" is written as $$$C_n^k$$$ in Russian notation, but as $$$C_k^n$$$ in English notation. We will probably rewrite the English tutorial using the $$$\binom{n}{k}$$$ notation instead of $$$C$$$ then.
interesting
You missed to fixed the index. Instead of l+2 you wrote r+2.
Man htf can one come with the optimization done in E for a O(n logn) solution as proving this things is a different things but coming with them is not and this is the first time I am seeing such a optimization . Figured out the whole idea for E but didn't knew how to optimize it during the contest btw
Could someone elaborate complexity proof in E?
You can refer to my stream for proof here
You initially have $$$1$$$ segment of indices of size $$$n$$$, and every time you split a segment of size $$$x$$$ into segments of sizes $$$k$$$ and $$$x-k$$$, you do $$$O(k)$$$ work. Let's "charge" that work to the first $$$k$$$ elements of a segment. Then observe that every time you charge an element, the new segment it is in is at most size $$$\lfloor\frac{x}{2}\rfloor$$$. Therefore each element can be charged at most $$$log_2(n)$$$ times, which gives a total bound of $$$nlog_2(n)$$$.
Let's say the number of operations to solve subarray $$$(l, r)$$$ of size $$$s = r-l$$$ is $$$F(s)$$$. If we find the minimum in this subarray at index $$$i = l+k$$$, The naive solution takes $$$k$$$ steps to find $$$i$$$ then solve subarrays of size about $$$k$$$ and $$$s-k$$$ respectively. This gives the recurrence $$$F(s) = k + F(k) + F(s-k)$$$ for some $$$k \in [0, s]$$$.
The worst case is $$$F(k) = O(k^2)$$$ which happens when $$$k$$$ always equals $$$s$$$, corresponding to a decreasing permutation.
The optimized solution just loops from both sides at the same time, which reduces the number of steps to find the $$$i$$$ from $$$k$$$ to $$$\min(k, s-k)$$$. The new recurrence is $$$F(s) = \min(k, s-k) + F(k) + F(s-k)$$$.
Letting $$$a = \min(k, s-k)$$$, we get $$$F(s) \leq a + 2F(a)$$$ for some $$$a \in [0, \frac{s}{2}]$$$. Because the the function $$$a + 2F(a)$$$ is clearly increasing, the worst case will happen when $$$a$$$ always equals $$$\frac{s}{2}$$$.
Let $$$G(s) = \frac{F(s)}{s}$$$, dividing by s on both sides and plugging in the worst case,
I think there might be an issue with the step
followed by
Since
and
assuming F(x) is non-decreasing we have
Therefore
not
Could you clarify how this inequality is obtained, or if there is an additional argument that I'm missing?
you are correct I am sad now :(
$$$T(n)\le\alpha\min(k, n - k) + T(k) + T(n - k)$$$ for some $$$\alpha$$$.
Assume by induction $$$T(i) \le C\cdot n\log n$$$ for some $$$C$$$ and all $$$i \lt n$$$, then :
Now by symmetry assume $$$k \lt \frac n 2$$$, you get $$$\frac{n}{k}\ge2$$$ and since $$$\frac{n}{n-k}\ge 1$$$ :
So it suffices to take $$$C \ge \dfrac\alpha{\log 2}$$$ to conclude.
My detailed 3 hour 15 minute detailed video editorial is now available here.
Can someone point out why I keep gettting TLE in probleme E here 377706411 ?
I thought in a similar way to the editorial and I implemented the
O(nlog(n))way, with the tiny difference that I computed the(i−l+1)(r−i+1)by adding a number in every iteration, but I don't think it influences the execution time.Thanks in advance!
i think it is because for (int i=l; i<=mid; i++) { bc time of work is N/2 + (N-1)/2 + (N-2)/2 ... + 1 = O(N^2)
How to optimize it into
O(n(logn))? I don't understand the solutionyou need to first check the leftmost element of the segment, then then rightmost, then the second leftmost, then the second rightmost and so on
Can you please give the proof how this effect time complexity? Problem E.
For D one can do a braindead memoized recursion. We only need to notice that there are 3 types of numbers: $$$a$$$, $$$b$$$, $$$a \oplus b$$$. Let's call them types: 1, 2, 3.
Let the answer for the problem be $$$f(a, b, k)$$$. Let $$$g(x)$$$ be the product of the number of set bits and the number of zero bits. What is the transition?
$$$f(a, b, k) = f(a, a \oplus b, k - 1) + f(a \oplus b, b, k - 1) - g(a \oplus b)$$$.
That is, in the types notation we have:
$$$f(1, 2, k) = f(1, 3, k - 1) + f(3, 2, k - 1) - g(3)$$$
Now, note that $$$f(t_1, t_2, k') = f(t_2, t_1, k')$$$, so we can only store the answer for the triplets $$$(t_1, t_2, k')$$$ with $$$t_1 \lt t_2$$$. One can use
mapto conveniently store such states.For each $$$k' \le k$$$ we need to store at most $$$3$$$ states, so the total number of recursion calls is at most $$$3k$$$.
ouch that's what I did lmao
Can someone explain problem C? I don't mean the solution, just the problem, I don't understand it...
Imagine you have $$$n$$$ containers (cylindrical and with the same area at the bottom) arranged in a circle with adjacent containers being connected by a tube (the tube's connection is at the same height in both containers). When you add water to a container, the water level rises, but if it exceeds the level of one of the tubes, then the water will start flowing through the tube. That means that if you keep filling the tube, then eventually the water level of both containers will be the same (assuming that the water doesn't escape to another container). The formalization just says that when the water level of at least one of the containers is above the tube that connects them, then their water levels must be equal because otherwise water would flow from the container of higher volume to the one of lower volume.
The task is to find a way to add water to the containers so that no water overflows into the container $$$i$$$ while maximizing the total amount of water in all the containers (the actual task is to find the amount of water not the way to fill the containers).
Is this explanation clear or is there a doubt I didn't address?
Now I understand it, thank you so much
Good compitition! Short code length of DEF made me became Master lol.
there is a sparse table + binary search approach to F too . that felt more intuitive to me . we can rotate the array, to get the peak at the end . each particular value will contribute to some elements only(that too will be in a contiguous segment), then we can use difference array and finally get the answer..
code. used gemini for the code during upsolving , cuz i fumbled implementation.
for E you can notice that the range of the first element always should be $$$[1, a_1]$$$, then then from that, you can find the range of the $$$a_1 + 1$$$-th element with a division as the length should be $$$\frac{a_{a_1 + 1}}{{a_1 + 1}}$$$ and from that you find the range corresponding to the $$$\frac{a_{a_1 + 1}}{{a_1 + 1}} + a_1 + 1$$$-th element and so on..., basically you can find all of the right parents of element 1 until the root (the element with the range $$$[1, n]$$$), we then mark everyone, and then do the same process for of $$$2, 3, ... , n$$$ each time you repeat the process until you reach the root or a marked element (for $$$2$$$ for example you can find all right parent until you reach some range that starts with $$$1$$$ which has been marked before). to satisfy all conditions for each division the numerator should be divisible by the denominator and also you should never go out of range, also after that you should check that the ranges you found satisfy the condition: "any two segments either dont intersect at all or one is contained in the other", you can sort the ranges in $$$O(n)$$$ or $$$O(n \log n)$$$.
$$$O(n)$$$ submission: 377742663
In Solution 2 of problem D, $$$F(l,A,B)=F(l−1,A,A⊕B)+F(l−1,A⊕B,B)$$$+[product of the $$$0$$$ and $$$1$$$ bit counts for $$$A⊕B$$$] should be replaced by $$$F(l,A,B)=F(l−1,A,A⊕B)+F(l−1,A⊕B,B)$$$ $$$\bf{-}$$$ [product of the $$$0$$$ and $$$1$$$ bit counts for $$$A⊕B$$$ ]. I think, it was typo. Also, the time complexity given is wrong I guess because for each $$$k$$$ we can have at max $$$6$$$ permutations of $$$A, B, C$$$ so, $$$6k$$$ states total. And, we need $$$O(\log k)$$$ time to calculate each state. Hence, time complexity should be $$$O(n + k\log k)$$$
Thank you, it is now fixed
Guys, i wonder if the technique used in problem E can be applied to other dnc like technique? I meant like what if instead of a permutation or an array, we are given the permutation of tree nodes? Can the same technique apply?
The formulation for problem C is too vague imo. On a second read this problem is very doable, but essentially it should be very intuitive.
It is not immediately readable how the sequences
h_iandw_irelate. In particular,h_irepresents the height of the connection between vesseliand vessel(i mod n) + 1(so, the next vessel in the circle), but this is not emphasized clearly enough. The term “partition” in the input description also feels a bit vague. “Connection” or something similar would fit better here.This causes the formal definition to feel difficult, when it should be trivial to understand. A better formulation for this would imo be:
Between vessel
iand vessel(i mod n) + 1, there is a connection at heighth_i. If the water level in either of the two vessels rises strictly above this connection height, then the two vessels must have equal water levels.l, we need to output the maximum possible value ofw_1 + w_2 + ... + w_n, over all good arrays satisfyingw_l = 0.So, for the plebs like me solving problem C:
You have a circle of water tanks, and they are connected to their neighbours with tubes. The heights of those tubes are given by the array
h, whereh_iis the height of the connection between vesseliand vessel(i mod n) + 1. Soh_1connects vessels1and2,h_2connects vessels2and3, ..., andh_nconnects vesselnback to vessel1, completing the circle.The array
wdescribes the water level in each vessel. The condition says: if the water level in either of two neighboring vessels is strictly above the height of the tube between them, then those two vessels must have the same water level.For each vessel
l, letG_lbe the set of all good arrayswsatisfyingw_l = 0, meaning vessellremains empty.For each
G_l, we want the maximum possible total amount of water across all vessels. Formally, this is the maximum possible value ofw_1 + w_2 + ... + w_n, over all arrays inG_l. Let this maximum value be denoted bym_l.Your goal is to output the sequence:
m_1 m_2 m_3 ... m_nNote Consider the first test case, where
n = 4andh = [1, 2, 3, 4].To keep vessel
1empty, the arrayw = [0, 0, 1, 0]is a good array inG_1, because no pair of neighboring vessels has a water level strictly above the height of the connection between them. Its total volume is1.However, this array is clearly not maximal. A maximal array in
G_1isw = [0, 1, 2, 3], with a total volume of6. Therefore,m_1 = 6.So
w = [0, 1, 2, 3]is a good array with sum6, and it can be shown that no array inG_1has a larger sum.Similarly:
2empty, one maximal good array isw = [1, 0, 2, 3], thus with am_2of6;3empty, one maximal good array isw = [2, 2, 0, 3], thus with am_3of7;4empty, one maximal good array isw = [3, 3, 3, 0], thus with am_4of9.Therefore, the final output is constructed from the indexed maximum good values
m_1 m_2 m_3 m_4:6 6 7 9Problem C had a very confusing explanation, that could use some work. The problem itself wasn't bad tho
Obviously the only numbers we will encounter are the first number $$$A$$$, the second number $$$B$$$, and their XOR $$$C$$$. Let’s simulate a few steps (it’s not hard to quickly write a script). For each step, let’s track the amount of $$$A$$$, $$$B$$$, and $$$C$$$ added, but let’s track $$$A + B$$$ because they’re symmetric:
It’s clear that the two changes share the same pattern, only different starting points. Let’s put it into OEIS!!! :money_mouth:
We find A078008 which has the following recurrence relation:
With the exception of the first step, the amount of $A$ and $$$B$$$ added on the $$$n$$$-th step is equal to $$$\frac{A_{n-1}}{2}$$$ and the amount of $$$C$$$ is equal to $$$A_{n-2}$$$. The rest is trivial, if you really need it check my AC submission.
Induction proof for $$$n \log n$$$ in E:
We spend $$$ck$$$ time iterating through the array to then recurse on problem sizes $$$n-k$$$, $$$k$$$. (I'm going to avoid big-O as much as possible because you can make errors with it easily because you might absorb constants too much and actually end up with an extra log or even exponential factor.) Suppose that by induction we can solve the problem size $$$k$$$ in $$$C k \log_2 k$$$ and $$$n-k$$$ in $$$C(n-k) \log_2 (n-k)$$$.
Then
$$$T(n) = T(n-k) + T(k) + ck,$$$
$$$T(n) \le C(n-k) \log_2(n) + Ck \log_2(k) + ck.$$$
We know that $$$k \le \frac{n}{2}$$$, so $$$\log_2 k$$$ is at most $$$\log_2(n) - 1$$$:
$$$T(n) \le C(n-k) \log_2(n) + Ck (\log_2(n) - 1) + ck,$$$
$$$T(n) \le Cn \log_2 n - Ck + ck.$$$
Now we see that $$$T(n) \le Cn \log_2 n$$$, as desired, if $$$C \ge c$$$.
I solved D without the observation that all numbers must be A, B or A^B.
For a fixed bitset t, #1 * #0 = $$$\sum_{i=1}^n\sum_{j=i+1}^nt[i] \oplus t[j]$$$.
Answer will be $$$\sum_{l=1}^{2^k+1}\sum_{i=1}^n\sum_{j=i+1}^na[l][i] \oplus a[l][j]=\sum_{i=1}^n\sum_{j=i+1}^n\sum_{l=1}^{2^k+1}a[l][i] \oplus a[l][j]$$$. $$$a[l][i]$$$ and $$$a[l][j]$$$ are dependent only on $$$a[1][i], a[2^k+1][i], a[1][j], a[2^k+1][j]$$$. There are only 16 combinations of values of $$$a[1][i], a[2^k+1][i], a[1][j], a[2^k+1][j]$$$, we can group (i,j) according to these values and calculate the answer. For a fixed quadruple we use dp given in the second solution, but each l will have 16 states at most as both A <= 3 and B <= 3 and we can do it explicitly.
E is beautiful, that optimization technique to show that search cost changes from O(n)->O(k) and now depends on smaller child which keeps halving because of the way we are iterating is very clever.
I actually solved it slightly differently by deducing the boundaries in $$$O(N)$$$ time.
First, the core observation for the formula: if an element at index $$$i$$$ is the minimum in the exclusive range $$$(l, r)$$$, it is the minimum for exactly $$$(i - l)$$$ valid left endpoints and $$$(r - i)$$$ valid right endpoints. Thus, $$$p[i] = (i - l) \times (r - i)$$$.Instead of searching for a valid root inside a known boundary, we can logically deduce the exact $$$(l, r)$$$ bounds for every element from left to right:Assume the array is padded with $$$-\infty$$$ at both ends. For index $$$0$$$, its previous smaller element is trivially $$$l = -1$$$.Since we know $$$p[0]$$$ and $$$l = -1$$$, we can directly calculate its next smaller element (NSE): $$$r = 0 + \frac{p[0]}{0 - (-1)}$$$.
This index $$$r$$$ gives us two things:Index $$$0$$$ is the absolute minimum of the subarray $$$[1, r-1]$$$ as its Next Smaller element was at $$$p[0]$$$ all the elements in b/w those elements were bigger than $$$0th$$$ element. We can recursively find the bounds for the inner elements $$$[1, r-1]$$$ as the bounds of this subarray is 0 and r which are obviously bigger then all the elements in the subarray giving us effectively $$$-\infty$$$ outer bounds. Now the element at $$$r$$$ is smaller than the element at $$$0$$$, the left boundary of r'th index is also $$$-1$$$ .We repeat the process for $$$r$$$. We use this to find its NSE, $$$r_1$$$, and recursively process the inner subarray $$$[r+1, r_1-1]$$$.By chaining this forward, we calculate the exact $$$[l, r]$$$ bounding box for every single element in $$$O(N)$$$ time.Now we mapped out the exact $$$(l, r)$$$ for every element, we can just build a reverse map: $$$ \lt l, r \gt $$$ -> index.To solve the subsegment $$$(L, R)$$$ finding the smallest index, we just query our reverse map.The key should be present exactly once in the subarray.If it does, let $$$i$$$ be the mapped index. We split the problem into $$$[L, i-1]$$$ and $$$[i+1, R]$$$, and multiply their results by $$$\binom{R - L - 2}{i - L - 1}$$$.
I also had a simillar solution to E (378239703), with the difference that ater calcualting (l,r), I created a tree of non-equalities —
uis a parent ofvif $$$u \lt v$$$ and there is no nodexsuch that $$$u \lt x \lt v$$$. After that I do a dp on the tree wheredp[u]=# of possible ways to arrange the subtree of u, where $$$dp[leaf]=1$$$ anddp[u] = MERGE all children of uwhere $$$MERGE(u,v)=dp[u]*dp[v]*(^{SIZ(u)+SIZ(v)}_{SIZ(u)})$$$. Computing $$$(^n_k)$$$ is done inO(log n), so my solution isO(n log n).My question is how do you avoid that
log npart in your solution?that's actually so good and easier to understand than author solution
technique of E is great, but i have no idea how to come up with something like that during the contest and not just believe but proof that
its classic dnc. patterns like this become very obvious over practice.
I think it comes with some practice with doing runtime analysis for divide and conquer algorithms (like doing recurrence relations algebraically and also recursion trees), and also just a bit of Russianness. Like in contest I was like "oh brute force search would work but it would be n^2 if the value was near the edges because the recursion tree would be too deep" -> "wait what if I just check the edges first" -> prove the complexity.
thanks! My main problem here is prove I think, I will practice it more. And I already have a lot of Russianness :)
I observed a interesting observation in D let $$$m = (2^k)+1$$$ The frequency value $$$A_1$$$ and $$$A_n$$$ and $$$Xor(A_1,A_n)$$$ will be $$$Ceil(m/3)$$$, $$$Ceil(m/3)$$$, $$$floor(m/3)$$$. so by this observation you can do it in $$$O(n)$$$ time complexity
in problem E, isn't it sufficient to find range maximum element on each range and check if it is really the count of subarrays?
Not always. Consider $$$n = 1001$$$, a min at the left or right edge would have 1001 arrays to be the min, but a min at the middle would have $$$501 \cdot 501 = 251{,}001$$$ arrays to be the min.
D is not 1500 are u kidding me :(
Actually C was worth 1400 D is 1100 or 1000 at max dude
How can someone think of the optimisation done in E it stills feels like n^2 to me
For D, can anyone explain how the solution is
O(n)? or any resources where I can understand the concept.Try to come up with a recursive solution that works in (n2) How n2 will pass here?
The python solution for problem C meets TLE. The same algorithm implemented in C/C++ is accepted.
In the problem C, the statement was very confusing. And very hard to visualize..