Codeforces Solutions 2: Prefix Sums, Strings, and Thought Transformation
Solutions from Dec 2023 to Jul 2024, covering prefix sums, strings, and transformation-oriented Codeforces problems.
Scope of Inclusion#
CF 1994 Div.1+2 CCF 1994 Div.1+2 DCF 1994 Div.1+2 ECF 1994 Div.1+2 FCF 1996 Div.3 DCF 1996 Div.3 ECF 1907 Div.3 DCF 1907 Div.3 ECF 1907 Div.3 FCF 1921 Div.3 FCF 1933 Div.3 ECF 1941 Div.3 DCF 1941 Div.3 FCF 1968 Div.3 FCF 1981 Div.2 CCF 1981 Div.2 DCF 1937 Div.2 D
CF 1994 Div.1+2 C - Hungry Games#
Yaroslav is playing a computer game. In one of the levels, he encounters mushrooms arranged in a row. Each mushroom has its own toxicity level; the toxicity level of the -th mushroom from the start is . Yaroslav can choose two integers , and then his character will eat the mushrooms in this segment one by one from left to right, i.e., mushrooms numbered .
The character’s toxicity level is , initially equal to . The computer game is defined by a number — the maximum toxicity level at any time. When eating a mushroom with toxicity level , the following happens:
- The character’s toxicity level increases by .
- If , the process continues; otherwise, becomes zero, and the process continues.
Yaroslav is interested in how many ways there are to choose the values of and such that the final value of is not zero. Help Yaroslav find this number!
Solution#
DP
For index , find the smallest subsequent index such that ,
i.e., , then there are types meeting the requirement.
, at this point, the result from index to is . If you continue eating, there might still be a result, which is
represents the number of valid intervals with left boundary .
DP transition equation:
#define int long long
void solve() {
int n, x; cin >> n >> x;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
partial_sum(a.begin() + 1, a.end(), a.begin() + 1);
vector<int> f(n + 2);
for (int i = n - 1; i >= 0; i--) {
int j = upper_bound(a.begin(), a.end(), a[i] + x) - a.begin();
f[i] = f[j] + j - i - 1;
}
cout << accumulate(f.begin(), f.end(), 0ll) << '\n';
}cppCF 1994 Div.1+2 D - Funny Game#
Vanya has a graph with vertices (numbered to ) and an array of integers; initially, the graph has no edges. Bored, Vanya decides to perform operations to have some fun.
Operation number (operations are numbered sequentially starting from ) is as follows:
- Choose different numbers such that is divisible by .
- Add an undirected edge between vertices and .
Help Vanya obtain a connected graph using operations, or determine if it’s impossible.
Solution#
Pigeonhole Principle
Operations need to be performed in reverse order (doesn’t affect the answer): Start from operation number (decreasing subsequently), need to find , i.e., .
According to the pigeonhole principle, it is always possible to find two points satisfying the requirement in each operation.
void solve() {
int n; cin >> n;
vector<int> a(n), p(n);
iota(p.begin(), p.end(), 0);
vector<pair<int, int>> ans;
for (auto& i : a) cin >> i;
for (int i = n - 1; i >= 1; i--) {
vector<int> occ(i, -1);
for (auto j : p) {
if (occ[a[j] % i] != -1) {
ans.push_back({j, occ[a[j] % i]});
p.erase(find(p.begin(), p.end(), j));
break;
}
occ[a[j] % i] = j;
}
}
cout << "YES\n";
reverse(ans.begin(), ans.end());
for (auto [x, y] : ans) cout << x + 1 << " " << y + 1 << "\n";
}cppCF 1994 Div.1+2 E - Wooden Game#
There is a forest consisting of rooted trees. Lumberjack Timofey wants to cut down the entire forest using the following operation:
- Choose any vertex’s subtree in any one of the trees and remove it from the tree.
Timofey loves bitwise operations, so he wants the bitwise OR ↗ of the sizes of the removed subtrees to be as large as possible. Please help him find the maximum possible result.
Solution#
The trees given in this problem are useless; only the vertex counts are useful.
For a certain tree, let the sizes of subtrees removed each time be . It’s easy to see: , and .
If there is no subtree of size , you can remove nodes from the subtree one by one to construct .
Therefore, for this tree, all answers in can be constructed.
For a certain tree, if the -th bit of is 1, then can be directly constructed. If the answer already has this part, then can be constructed, thus ensuring optimality.
Very clever!
void solve() {
int k; cin >> k;
vector<int> a(k);
for (int i = 0; i < k; i++) {
cin >> a[i];
for (int j = 0; j < a[i] - 1; j++) {
int x; cin >> x;
}
}
sort(a.rbegin(), a.rend());
int ans = 0;
for (auto x : a) {
for (int i = __lg(x); i >= 0; i--) {
if ((x >> i) & 1) {
int y = (ans >> i) & 1;
if (y == 0) {
ans |= (1 << i);
} else {
ans |= (1 << i) - 1;
break;
}
}
}
}
cout << ans << '\n';
}cppCF 1994 Div.1+2 F - Stardew Valley#
Pelican Town consists of houses connected by bidirectional roads. On some roads, there are NPCs standing. Farmer Bubba needs to walk along each road with an NPC and talk to them.
Help the farmer find a route with the following properties:
- The route starts at a house, follows roads, and ends at the same house.
- Each road can be traversed at most once.
- All roads containing NPCs must be traversed, while roads without NPCs don’t have to be.
It is guaranteed that one can reach any other house from any other house by walking only on roads with NPCs.
Solution#
Eulerian Graph
Similar problem: F - Perils in Parallel ↗
The problem means this graph is the complete set of NPC edges plus a subset of non-NPC edges (i.e., delete some non-NPC edges), find an Eulerian circuit of this graph.
The condition for having an Eulerian graph is that the degree of every vertex is even.
For deleting a specific edge, you can use the technique in the code:
g[u].push(i), edge[i]=u^v->int to=edge[i]^u.
For this problem, you might need to delete some edges from the graph composed of non-NPC edges to do the Eulerian circuit. If the degree of every vertex in the graph is even, then an Eulerian circuit definitely exists; otherwise, it definitely doesn’t exist.
Construct a graph from the non-NPC edges. Process this graph. If all vertex degrees can be made even, then proceed to find the Eulerian circuit.
If there is an odd number of vertices with odd degree, it’s impossible to make all degrees even by deleting edges. During the search, if a certain vertex has an odd degree, you need to delete an edge between this vertex and its connected vertex.
void solve() {
int n, m; cin >> n >> m;
vector<vector<int>> g(n + 1), blk(n + 1);
vector<int> edge(m + 1);
for (int i = 1; i <= m; i++) {
int u, v, is; cin >> u >> v >> is;
g[u].push_back(i);
g[v].push_back(i);
edge[i] = u ^ v;
if (!is) {
blk[u].push_back(i);
blk[v].push_back(i);
}
}
vector<int> deg(n + 1);
for (int i = 1; i <= n; i++) deg[i] = g[i].size() & 1;
vector<int> del(m + 1), vis(n + 1);
auto dfs = [&](auto self, int u)->void {
vis[u] = 1;
for (auto x : blk[u]) {
int to = edge[x] ^ u;
if (vis[to]) continue;
self(self, to);
if (deg[to]) {
del[x] = 1;
deg[to] ^= 1;
deg[u] ^= 1;
}
}
};
int ok = 1;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
dfs(dfs, i);
ok &= !deg[i];
}
if (!ok) {
cout << "NO\n"; return;
}
cout << "YES\n";
cout << m - accumulate(del.begin() + 1, del.end(), 0) << '\n';
auto euler = [&](auto self, int u) ->void {
while (g[u].size()) {
int x = g[u].back(); g[u].pop_back();
int to = edge[x] ^ u;
if (del[x]) continue;
del[x] = 1;
self(self, to);
}
cout << u << " ";
};
euler(euler, 1);
cout << '\n';
}cppCF 1996 Div.3 D - Fun#
Given two integers and , find the number of triples () of positive integers such that and .
Note that means order matters and .
Solution#
This problem is simple; I overcomplicated it, always thinking the complexity was not right hh.
Knowing , calculate the maximum that satisfies the conditions, so the number of types is added by .
for i i <= n i++
for j i * j <= n j++cppNumber of operations: times.
void solve() {
int n, x; cin >> n >> x;
int sum = 0;
for (int a = 1; a <= min(x - 2, n); a++) {
for (int b = 1; a * b < n && a + b < x; b++) {
sum += min((n - a * b) / (a + b), x - a - b);
}
}
cout << sum << "\n";
}cppCF 1996 Div.3 E - Decode#
To get your “waifu” favorite character, you desperately hack into the game’s source code. After days of effort, you finally find the binary string encoding the game’s gacha system. To decode it, you must first solve the following problem.
You are given a binary string of length . For every pair of integers , you need to count how many pairs of integers such that the number of s equals the number of s in the substring .
Output the sum of these counts over all possible modulo .
Solution#
When encountering problems requiring equality like this, it’s somewhat similar to Niuke Weekly Contest 52-F.
If , set , otherwise set it to 1. This way, for an interval , directly check if is 0, i.e., .
The problem becomes: For all intervals , count the sum of the number of pairs satisfying .
If for an interval the number of 0s equals the number of 1s, i.e., , then there are ways.
That is, any left endpoint in can be chosen, and any right endpoint in can be chosen.
That is:
for (int l = 1; l <= n; l++) {
for (int r = l; r <= n; r++) {
if (pre[r] == pre[l - 1]) {
ans += l * (n - r + 1); ans %= mod;
}
}
}cppUsing a DP-like idea, by enumerating , accumulate the sum of each time.
represents the sum of indices in that satisfy , i.e., .
#define int long long
constexpr int mod = 1e9 + 7;
void solve() {
string s; cin >> s; int n = s.size();
vector<int> pre(n + 1);
for (int i = 0; i < n; i++) {
pre[i + 1] = pre[i] + (s[i] == '0' ? -1 : 1);
}
vector<int> f(2 * n + 1);
int ans = 0;
for (int i = 0; i <= n; i++) {
int t = pre[i] + n;
ans += f[t] * (n - i + 1); ans %= mod;
f[t] += i + 1; f[t] %= mod;
}
cout << ans << '\n';
}cppCF 1907 Div.3 D - Jumping Through Segments#
Find the minimum movement distance such that after each move, you can stay within the target interval .
In the player can take the following actions:
- Move from point to point ();
- Move from point to point ();
- Move from point to point (). Note that for the last step, the player can choose not to move and still complete the level.
Perform a binary search between until finding the optimal .
check function:
Used to check if, given a length , there exists a way to cover all segments.
- Initialize
llandrrto 0, representing the left and right boundaries of the current coverage interval. - For each segment :
a. Calculate the left boundary of the new coverage interval as
max (ll - k, a), indicating moving as far left as possible while ensuring coverage. b. Calculate the right boundary of the new coverage interval asmin (rr + k, b), indicating moving as far right as possible while ensuring coverage. c. If the left boundary of the new coverage interval is greater than the right boundary, returnfalse, indicating it’s impossible to cover all segments. - If all segments are traversed successfully and can be covered, return
true, indicating there exists a way to cover all segments within length . The role of thecheckfunction is crucial, but proving its correctness remains a question for me.
Binary search:
Changing the 2D vector<vector<int>> to vector<array<int, 2>> saves a lot of space.
#include<bits/stdc++.h>
using namespace std;
bool check(int k, vector<array<int, 2>> &seg)
{
int ll = 0, rr = 0;
for (auto [a,b] : seg)
{
ll = max(ll - k, a), rr = min(rr + k, b);
if (ll > rr) return false;
}
return true;
}
void solve()
{
int n;
cin >> n;
vector<array<int,2>> seg(n);
for (int i = 0; i < n; i++)
{
int a, b;
cin >> a >> b;
seg[i] = {a, b};
}
int l = -1, r = 1000000000;
while (r - l > 1)
{
int mid = (r + l) / 2;
if (check(mid, seg))
r = mid;
else
l = mid;
}
cout << r << endl;
}
int main()
{
int t;
cin >> t;
while(t--)
solve();
}cppCF 1907 Div.3 E - Good Triples#
Given , find the number of triples satisfying . represents the sum of digits of .
| n | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| ans | 1 | 3 | 6 | 10 | 15 | 21 | 28 | 36 | 45 | 55 |
For , .
int x=n%10, ans *= (x + 1) * (x + 2) / 2, n/=10;
#include <bits/stdc++.h>
using namespace std;
int t, a[] = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55};
int main()
{
cin >> t;
while (t--)
{
long long ans = 1;
int n;
cin >> n;
while (n)
ans *= a[n % 10], n /= 10; // ans *= (x + 1) * (x + 2) / 2, n/=10; //written by jiangly
cout << ans << '\n';
}
}cppCF 1907 Div.3 F - Shift and Reverse#
Given an integer array . You can perform two types of operations on this array:
- Shift: Move the last element of the array to the first position, and shift all other elements to the right, so you get the array .
- Reverse: Reverse the entire array, so you get .
Your task is to sort the array in non-decreasing order using the minimum number of operations. If impossible, output
-1.
Let’s write the array out twice, then calculate the increasing and decreasing parts of the array. This way, we can find all possible moves that can sort the array.
down: non-increasing
- If starting from position
st, the arraya[st]toa[st + n - 1]is non-increasing, then we only need to move this sequence segment to the front of the array, and then reverse the entire array. The number of shifts isst + 1, and the number of reverses is1. Since each shift moves the last element to the first, the minimum number of operations for this part ismin(st + 1, n - st + 1), representing shifting left or right. Then reverse once, so the total number of operations isst + 1orn - st + 1.
up: non-decreasing
- If starting from position
st, the arraya[st]toa[st + n - 1]is non-decreasing, we only need to move this sequence segment to the front of the array. The number of shifts isst + 1. However, since each shift moves the last element to the first, we need to reverse once. Thus, the total number of operations isst + 2. Another scenario is that we can still choose to shift in the other direction, i.e., push then - stelements from the end backward, then reverse once, the number of operations isn - st + 1. But considering that the positionstis definitely non-decreasing, it can be directly shifted to the front of the queue, then reverse the entire array, the number of operations isn - st. So the minimum number of operations for this part ismin(st + 2, n - st). Thus, we get the minimum number of operations that can make the entire array non-decreasing starting from each position. Taking the minimum among all cases gives the answer.
//core:
when down:
ans = min({ans, st + 1, n - st + 1});
when up:
ans = min({ans, st + 2, n - st});cpp#include <bits/stdc++.h>
using namespace std;
int t, n;
int main()
{
cin >> t;
while (t--)
{
cin >> n;
vector<int> a(2 * n, 0);
for (int i = 0; i < n; i++)
cin >> a[i];
copy(a.begin(), a.begin() + n, a.begin() + n);
if (is_sorted(a.begin(), a.begin() + n))// special case
{
cout << '0' << '\n';
continue;
}
int ans = INT_MAX;
for (int i = 0; i < n; i++) // down
{
int st = i, cnt = 1;
while (i <= 2 * n - 1 && a[i] >= a[i + 1])
i++, cnt++;
if (cnt >= n)
ans = min(ans, min(st + 1, n - st + 1));
}
for (int i = 0; i < n; i++) // up
{
int st = i, cnt = 1;
while (i <= 2 * n - 1 && a[i] <= a[i + 1])
i++, cnt++;
if (cnt >= n)
ans = min(ans, min(st + 2, n - st));//(if(!st)ans=0;)<->(special case)
}
if (ans == INT_MAX)
cout << "-1" << '\n';
else
cout << ans << '\n';
}
}cppCF 1921 Div.3 F - Sum of Progression#
You are given an array of numbers. There are also queries of the form .
For each query , find .
For each test case, the input is in the form:
n q
a1 a2 ... an
s1 d1 k1
s2 d2 k2
...
sq dq kqtextSolution#
Square Root Decomposition
Set a threshold:
When , just do it brute force (because the number of operations is very small at this point).
When , open arrays to compute prefix sums. (Let represent respectively)
- Array records the prefix sum of , recording
- Array records the prefix sum of , recording
- Difference
The handling method might be more than one…
int n,q,a[100010];
ll f[100010][350],g[100010][350];//index step d
(ios::sync_with_stdio(false), cin.tie(nullptr);)//must turn off sync, otherwise TLE
void solve()
{
cin >> n >> q;
int value = sqrt(n);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int d = 1; d <= value; d++){
for (int i = 1; i <= n; i++){
f[i][d] = ((i - d > 0) ? f[i - d][d] : 0ll) + 1ll * a[i] * (i / d); //((i - 1) / d + 1); The gray part can also work
g[i][d] = ((i - d > 0) ? g[i - d][d] : 0ll) + a[i];
}
}
ll ans;
while (q--){
cin >> s >> d >> k;
ans = 0;
if (d > value){
for (int i = 0; i < k; i++)
ans += 1ll * a[s + i * d] * (i + 1);
}
else{
ans = f[s + d * (k - 1)][d] - ((s - d > 0) ? f[s - d][d] : 0);
ans -= (g[s + d * (k - 1)][d] - ((s - d > 0) ? g[s - d][d] : 0)) * (s / d - 1); //((s - 1) / d);
}
cout << ans << '\n';
}
}cppCF 1933 Div.3 E - Turtle vs. Rabbit Race: Optimal Trainings#
Isaac begins his training. There are tracks available for use, and the -th track () consists of equal-length sections.
Given an integer (), completing each section increases Isaac’s ability by a specific value, described as follows:
- Completing the -st section increases Isaac’s ability by .
- Completing the -nd section increases Isaac’s ability by .
- Completing the -rd section increases Isaac’s ability by .
- Completing the -th section () increases Isaac’s ability by . The value can be negative, meaning completing additional sections can decrease Isaac’s ability.
You are also given an integer . You must choose an integer such that and Isaac completes all sections of tracks (i.e., a total of sections).
Answer the following question: What is the best you can choose to maximize Isaac’s ability increase?
If there are multiple that maximize Isaac’s ability increase, choose the smallest .
To increase the difficulty, you need to answer queries for different values of and .
Solution#
Binary Search/Ternary Search
Ternary search template problem
Since the gain for this problem is: when , the gain curve first rises then falls. Just find the corresponding to the highest point.
#define int long long
void solve() {
int n; cin >> n; vector<int> a(n + 1), pre(n + 1); for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + a[i];
int q; cin >> q;
auto f = [&](int l, int r, int u) {// sum of first n terms of arithmetic progression u, u-1, u-2... u+1-k
return (u + u + 1 - pre[r] + pre[l - 1]) * (pre[r] - pre[l - 1]) / 2;
};
while (q--) {
int L, u; cin >> L >> u;
int l = L, r = n;
while (l < r) {
int lmid = l + (r - l) / 3;
int rmid = r - (r - l) / 3;
if (f(L, lmid, u) < f(L, rmid, u)) l = lmid + 1;
else r = rmid - 1;
}
cout << l << ' ';
}
cout << '\n';
}cppi64 calc(int u, int x) {
return 1LL * (u + u - x + 1) * x / 2;
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::vector<int> s(n + 1);
for (int i = 0; i < n; i++) {
s[i + 1] = s[i] + a[i];
}
int q;
std::cin >> q;
while (q--) {
int l, u;
std::cin >> l >> u;
l--;
int j = std::lower_bound(s.begin() + l + 1, s.end(), s[l] + u) - s.begin();
i64 ans = -1E18;
int r = -1;
if (j <= n) {
if (calc(u, s[j] - s[l]) > ans) {
ans = calc(u, s[j] - s[l]);
r = j;
}
}
if (j - 1 > l) {
if (calc(u, s[j - 1] - s[l]) >= ans) {
ans = calc(u, s[j - 1] - s[l]);
r = j - 1;
}
}
std::cout << r << " \n"[q == 0];
}
}cppCF 1941 Div.3 D - Rudolf and the Ball Game#
Rudolf and Bernard decided to play a game with their friends. people stand in a circle and start throwing a ball to each other. They are numbered from to in clockwise order.
Let’s call a transition the movement of a ball from one person to their neighbor. The transition can be made clockwise or counterclockwise.
Initially, the ball is with the player number (players are numbered clockwise). At the -th step, the person in possession of the ball throws it at a distance of () either clockwise or counterclockwise. For example, if there are players, and the -nd player in possession throws the ball a distance of , then the ball will be caught by either the -th player (throwing clockwise) or the -th player (throwing counterclockwise). An illustration of this example is shown below.
Due to rain, the game was interrupted after throws. When the rain stopped, everyone gathered again to continue the game. However, no one remembered who had the ball. As a result, Bernard remembered the distance and the direction (clockwise or counterclockwise) for each of the throws.
Rudolf asks you to help him and, based on Bernard’s information, calculate the number of players who could have the ball after throws.
Solution#
DP/Thinking
Although this problem didn’t use dp, DP needs focused practice!
Jiangly’s approach for this problem is great: update the reachable positions in each step into a d array, then assign all reachable positions from the d array to the dp array. This way, the dp array holds all reachable final positions.
void solve() {
int n, m, x; cin >> n >> m >> x; x--;
vector<int> dp(n); dp[x] = 1;
for (int i = 0; i < m; i++) {
int d; char ch; cin >> d >> ch;
vector<int> g(n);
for (int j = 0; j < n; j++) {
if (dp[j]) {
if (ch != '1') {
g[(j + d) % n] = 1;
}
if (ch != '0') {
g[(j - d + n) % n] = 1;
}
}
}
dp = g;
}
cout << count(dp.begin(), dp.end(), 1) << '\n';
for (int i = 0; i < n; i++) {
if (dp[i]) cout << i + 1 << ' ';
}
cout << '\n';
}cppCF 1941 Div.3 F - Rudolf and Imbalance#
Given arrays of lengths respectively. is sorted in ascending order. You can take one number from array and one from array and insert them into array , then sort it in ascending order.
Find the minimum possible value of the maximum difference between consecutive elements after this operation.
Solution#
Binary Search + Two Pointers
Since only one operation is allowed, it must target the position with the largest gap . The second largest gap will not be operated on, so the answer after the operation must be at least the original second largest .
First, find the index corresponding to in the sequence.
For , we always want to “neutralize” this maximum gap. The best case is that the chosen numbers from arrays and sum to exactly . Even if we can’t achieve that exact value, we should get as close as possible.
Find the maximum and second maximum values as backup, then perform binary search. The binary search will yield two values at the exit; compare them and take the minimum.
#define int long long
void solve() {
int n, m, k; cin >> n >> m >> k;
vector<int> a(n), b(m), c(k);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < m; i++) cin >> b[i];
for (int i = 0; i < k; i++) cin >> c[i];
sort(b.begin(), b.end());
sort(c.begin(), c.end());
int ma = 0, p = -1;
for (int i = 1; i < n; i++) {
if (a[i] - a[i - 1] > ma) {
ma = a[i] - a[i - 1];
p = i;
}
}
int ans = ma;
ma = 0;
for (int i = 1; i < n; i++) {
if (i != p && a[i] - a[i - 1] > ma) {
ma = a[i] - a[i - 1];
}
}
int t = (a[p] + a[p - 1]) >> 1;
for (int i = 0; i < m; i++) {
int l = 0, r = k - 1;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (b[i] + c[mid] <= t) {
l = mid;
} else {
r = mid;
}
}
ans = min(ans, max({ma, abs(b[i] + c[l] - a[p - 1]), abs(b[i] + c[l] - a[p])}));
ans = min(ans, max({ma, abs(b[i] + c[r] - a[p - 1]), abs(b[i] + c[r] - a[p])}));
}
cout << ans << '\n';
}cppCF 1968 Div.3 F - Equal XOR Segments#
An array is called interesting if it can be divided into arrays.
Specifically, it can be divided into parts, such that the bitwise XOR ↗ of the values in each part is equal.
More formally, you must split array into contiguous segments, and each element of must belong to exactly segment. Let be the XOR of the elements in each part. Then we must have .
For example, if , you can split it as follows: . Indeed, .
You are given an array . Your task is to answer queries:
- For a fixed , , determine if the subarray is interesting.
Solution#
Bitwise operations
Properties of : If there is an odd number of intervals with equal XOR sum, then the XOR of all of them equals the XOR of each individual interval.
In this problem, if there are three or more intervals with equal XOR sum, these three intervals can be merged into one interval, and the XOR sum will be the same as before.
Since the problem requires , the range for is only . If , it can always be reduced to using the method above.
Let be the prefix XOR sum.
When , the interval can be divided into two parts with equal XOR sum . Then .
When , divide the interval into . Then and .
and
To satisfy this condition, we need as small as possible and as large as possible, provided the values are equal. Let be the smallest index , and be the largest index .
I got this reversed here:
lower_boundgives the first element >= value,upper_boundgives the first element > value.
void solve() {
int n, q; cin >> n >> q; vector<int> a(n + 1), s(n + 1);
map<int, vector<int>> mp;
mp[0].push_back(0);
for (int i = 1; i <= n; i++) {
cin >> a[i]; s[i] = s[i - 1] ^ a[i];
mp[s[i]].push_back(i);
}
while (q--) {
int l, r; cin >> l >> r;
if (s[r] == s[l - 1]) {
cout << "Yes\n";
} else {
auto t = *lower_bound(mp[s[r]].begin(), mp[s[r]].end(), l);
auto m = *--lower_bound(mp[s[l - 1]].begin(), mp[s[l - 1]].end(), r);
if (t < m) {
cout << "Yes\n";
} else {
cout << "No\n";
}
}
}
cout << '\n';
}cppCF 1981 Div.2 C - Turtle and an Incomplete Sequence#
Complete the positions where , so that for every integer from to , either or holds. If there is no solution, output .
This problem can be considered a large simulation or a thinking problem.
jiangly’s Approach:#
First, fill in the s at both ends. For the middle blocks, each block is defined by indices representing that the interval is all . Fill from both ends towards the middle. If upon reaching the middle, two adjacent numbers don’t satisfy the condition, output . Otherwise, continue to the next block.
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
if (count(a.begin(), a.end(), -1) == n) {
for (int i = 0; i < n; i++) {
a[i] = i % 2 + 1;
}
} else {
for (int i = 0, j = -1; i <= n; i++) {
if (i == n || a[i] != -1) {
if (j == -1) {
for (int k = i - 1; k >= 0; k--) {
a[k] = a[k + 1] == 1 ? 2 : a[k + 1] / 2;
}
} else if (i == n) {
for (int k = j + 1; k < n; k++) {
a[k] = a[k - 1] == 1 ? 2 : a[k - 1] / 2;
}
} else {
int l = j, r = i;
while (l + 1 < r) {
if (a[l] > a[r]) {
a[l + 1] = a[l] == 1 ? 2 : a[l] / 2;
l++;
} else {
a[r - 1] = a[r] == 1 ? 2 : a[r] / 2;
r--;
}
}
if (a[l] != a[r] / 2 && a[r] != a[l] / 2) {
cout << -1 << "\n";
return;
}
}
j = i;
}
}
}
for (int i = 0; i < n; i++) {
cout << a[i] << " \n"[i == n - 1];
}
}cppOfficial Editorial:#
Consider each transformation as a move on a complete binary tree. This becomes a shortest path problem.
The path from to is . Let the number of s between them be . If the number of nodes passed on the path is , then if or the parity of and is different, there’s no solution. Otherwise, first fill the initial empty spaces, then cycle between the two numbers.
Code: Similarly, fill the beginning and end first, then handle each block separately.
First, calculate the path between the two ends of this block. Process according to the idea.
The path function performs operations on a full binary tree:
auto path = [](int x, int y)->vector<int> {
vector<int> l, r;
while (__lg(x) > __lg(y)) {
l.push_back(x); x >>= 1;
}
while (__lg(y) > __lg(x)) {
r.push_back(y); y >>= 1;
}
while (x != y) {
l.push_back(x);
r.push_back(y);
x >>= 1; y >>= 1;
}
l.push_back(x);
reverse(r.begin(), r.end());
for (auto x : r) l.push_back(x);
return l;
};cppvoid solve() {
int n; cin >> n;
vector<int> a(n + 1), v;
int s = 0, e = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] != -1) {
if (!s) s = i;
e = i;
v.push_back(i);
}
}
if (count(a.begin(), a.end(), -1) == n) {
for (int i = 1; i <= n; i++) {
cout << i % 2 + 1 << " \n"[i == n];
}
return;
}
for (int i = s - 1; i >= 1; i--) {
a[i] = (a[i + 1] == 1) ? 2 : a[i + 1] / 2;
}
for (int i = e + 1; i <= n; i++) {
a[i] = (a[i - 1] == 1) ? 2 : a[i - 1] / 2;
}
for (int i = 1; i < v.size(); i++) {
int l = v[i - 1], r = v[i];
vector<int> p = path(a[l], a[r]);
if ((p.size() & 1) != ((r - l + 1) & 1) || r - l + 1 < p.size()) {
cout << "-1\n"; return;
}
for (int j = 0; j < p.size(); j++) {
a[l + j] = p[j];
}
for (int j = l + p.size(), o = 1; j <= r; j++, o ^= 1) {
a[j] = (o ? a[j - 1] * 2 : a[j - 1] / 2);
}
}
for (int i = 1; i <= n; i++) {
cout << a[i] << " \n"[i == n];
}
}cppCF 1981 Div.2 D - Turtle and Multiplication#
Given an integer (), construct a sequence of integers satisfying the following conditions:
- For all , .
- For all , .
Among all such sequences, Turtle asks for the one with the minimum number of distinct elements.
Solution#
Eulerian Path / Hierholzer’s Algorithm
Need to learn Eulerian graph相关知识, Note: This part is relatively niche.
Eulerian graph problem
To make , simply take all as primes. This way, we just need and to be different (i.e., traversing distinct edges).
Treat as an edge in a graph. The problem then becomes finding an undirected complete graph (plus self-loops ) with the minimum number of vertices, such that this graph has a path traversing edges without repeating edges.
If the number of vertices in the complete graph is . (Here the number of vertices is determined, need to maximize the number of edges)
-
If is odd, then the degree of each vertex is even, so an Eulerian path definitely exists, and the path length is .
-
If is even, then the degree of each vertex is odd. At this point, we need to delete some edges to ensure at least an Eulerian trail exists. At most, only two vertices can have odd degree, so at least edges must be deleted. The path length becomes .
When , , and the -th prime is , satisfying the requirement.
Thus, we only need to determine the minimum , and then run an Eulerian trail.
For the minimum , satisfying:
Code:
void solve() {
int n;
cin >> n;
int m = 1;
while (n - 1 > (m % 2 == 1 ? m * (m + 1) / 2 : m * m / 2 + 1)) {
m++;
}
vector<int> a;
a.reserve(n);
vector<vector<int>> g(m, vector<int>(m, 1));
vector<int> cur(m);
if (m % 2 == 0) {
for (int i = 1; i < m - 1; i += 2) {
g[i][i + 1] = g[i + 1][i] = 0;
}
}
auto dfs = [&](auto&& self, int x) -> void {
for (int& i = cur[x]; i < m; i++) {
if (g[x][i]) {
g[x][i] = g[i][x] = 0;
self(self, i);
}
}
a.push_back(primes[x]);
};
dfs(dfs, 0);
a.resize(n);
for (int i = 0; i < n; i++) {
cout << a[i] << " \n"[i == n - 1];
}
}cppA sieve is needed here; Template organization
CF 1937 Div.2 D - Pinball#
There is a one-dimensional grid of length . The -th cell of the grid contains a character , which is either ’<’ or ’>’.
When a pinball is placed on one of the cells, it moves according to the following rules:
- If the pinball is on the -th cell and is ’<’, the pinball moves one cell to the left in the next second. If is ’>’, it moves one cell to the right.
- After the pinball moves, the character is reversed (i.e., if was originally ’<’, it becomes ’>’, and vice versa).
- The pinball stops moving when it leaves the grid: either from the left boundary or the right boundary.
You need to answer independent queries. In the -th query, the pinball is placed on the -th cell. Note that we always place a pinball on the initial grid.
For each query, calculate how many seconds it takes for the pinball to leave the grid. It can be proven that the pinball will always leave the grid in a finite number of steps.
Solution#
Original: Problem - E - Codeforces ↗
Codeforces Round 930 (Div. 2) A~D - Zhihu ↗
Need to record the indices of > and prefix sums of counts, as well as indices of < and suffix sums of counts.
Only the > on the left and < on the right can make s[i] reverse; if not, it goes out directly.
When s[i]=='>':
- If , then the answer is .
- If , then the pinball at position will definitely exit from the right. The path is fully traversed only when the left count exceeds the right count by 1. In other cases, some paths are not traversed, so the excess part needs to be subtracted. When fully traversed, the answer is . When there is an excess part, the answer is .
- If , then the pinball at position will definitely exit from the left. Some excess parts on the right are not traversed at all, so they need to be subtracted. The answer is .
When s[i]=='<', the analysis is analogous.
#define int long long
void solve() {
int n; cin >> n; string s; cin >> s; s = ' ' + s;
vector<int> pre(n + 2), suf(n + 2), pos1, pos2, cnt1(n + 2), cnt2(n + 2), ans(n + 1);
for (int i = 1; i <= n; i++) {
if (s[i] == '>') {
pre[i] = i; cnt1[i] = 1;
} else {
suf[i] = i; cnt2[i] = 1;
}
}
for (int i = 1; i <= n; i++) {
cnt1[i] += cnt1[i - 1];
pre[i] += pre[i - 1];
if (s[i] == '>') pos1.push_back(i);
}
for (int i = n; i >= 1; i--) {
cnt2[i] += cnt2[i + 1];
suf[i] += suf[i + 1];
if (s[i] == '<') pos2.push_back(i);
}
for (int i = 1; i <= n; i++) {
if (s[i] == '<') {
if (cnt1[i - 1] > cnt2[i + 1]) { // exits to the right
int num2 = suf[i + 1], num1 = 0;
if (cnt1[i - 1] == cnt2[i + 1] + 1) {
num1 = pre[i - 1];
} else {
num1 = pre[i - 1] - pre[pos1[cnt1[i - 1] - cnt2[i + 1] - 2]];
}
ans[i] = i + n + 1 + 2 * (num2 - num1);
} else if (cnt1[i - 1] < cnt2[i + 1]) { // exits to the left
int num1 = pre[i - 1];
int num2 = suf[i + 1] - suf[pos2[cnt2[i + 1] - cnt1[i - 1] - 1]];
ans[i] = i + 2 * (num2 - num1);
} else {
int num1 = pre[i - 1], num2 = suf[i + 1];
ans[i] = i + 2 * (num2 - num1);
}
} else {
if (cnt1[i - 1] < cnt2[i + 1]) {
int num1 = pre[i - 1], num2 = 0;
if (cnt2[i + 1] == cnt1[i - 1] + 1) {
num2 = suf[i + 1];
} else {
num2 = suf[i + 1] - suf[pos2[cnt2[i + 1] - cnt1[i - 1] - 2]];
}
ans[i] = 2 * (num2 - num1) - i;
} else if (cnt1[i - 1] > cnt2[i + 1]) {
int num1 = pre[i - 1] - pre[pos1[cnt1[i - 1] - cnt2[i + 1] - 1]];
int num2 = suf[i + 1];
ans[i] = n + 1 - i + 2 * (num2 - num1);
} else {
int num1 = pre[i - 1], num2 = suf[i + 1];
ans[i] = n + 1 - i + 2 * (num2 - num1);
}
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " \n"[i == n];
}
}cpp