Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Don’t try to simulate the operations plant-by-plant. The useful thing about is that the value of only depends on the exact power of two dividing .
Split every update by , where . For a fixed , this happens exactly when .
For an operation starting at , the position has offset . So for fixed , affected positions satisfy On those positions, the added value is which is just a linear function of .
So the whole problem becomes: many updates of “add to all positions in with one residue modulo .” Since the moduli are powers of two up to about , you can keep one difference array per residue class for each modulus.
For each power and residue , compress positions with that residue into an array: . A range update on actual positions becomes a contiguous segment in this compressed array. Store two lazy difference arrays: one for the coefficient of , one for the constant term.
We need process range updates where the added pattern is not constant, not linear everywhere, and not even periodic by itself. So the obvious “difference array and vibe” plan dies immediately. The escape hatch is the lowbit.
For an offset , write
That means is divisible by but not by , equivalently
Then
So if we group offsets by their lowbit, the weird function becomes a linear function. That is the whole trick. Not magic, just binary being annoyingly helpful.
Splitting One Operation
Consider one watering operation . Plant receives
Let
For a fixed , the offsets with lowbit satisfy
Substitute :
so
On exactly those positions, the contribution is
That is of the form
where
So every original update becomes about updates of this form:
add to positions having one specific residue modulo .
Handling Residue-Class Range Updates
Fix a modulus
For every residue , the positions with that residue are
inside .
Inside this list, any actual interval becomes one contiguous segment. Nice. So for each pair , we can keep difference arrays over that compressed list.
Because each update adds , we need two difference arrays:
After all updates, prefix-sum those difference arrays. If position currently has coefficient sum and constant sum , then it receives
Add that into the answer.
Finding The Segment In A Residue List
For a fixed modulus and residue , define the first positive position with that residue. Since positions are -indexed, residue means the first position is , otherwise it is .
Call this first position .
The compressed index of a position with the same residue is
For an update interval , we need the first valid position at least and the last valid position at most .
We can compute them with small helper functions:
first_ge(l, p, rem) gives the first number with that residue;last_le(r, p, rem) gives the last number with that residue.If the first one is after the last one, that residue contributes nothing for this update.
Why The Size Is Fine
The moduli are powers of two:
For modulus , there are residue classes, but the total number of stored positions across all residues is just . Across all powers, that is
Each operation creates one update per relevant , also .
So the total complexity is
per total input size, with memory also . For , that’s completely fine.
Overflow Check
A single contribution can be around
and there can be operations. This easily goes past 32-bit integers. Use long long. Letting int handle this would be a beautiful way to get wrecked by arithmetic, not by algorithms.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
int levels = 0;
while ((1 << levels) <= n) levels++;
vector<vector<vector<ll>>> da(levels), db(levels);
for (int k = 0; k < levels; k++) {
int p = 1 << (k + 1);
da[k].resize(p);
db[k].resize(p);
for (int rem = 0; rem < p; rem++) {
int first = (rem == 0 ? p : rem);
if (first > n) continue;
int cnt = (n - first) / p + 1;
da[k][rem].assign(cnt + 1, 0);
db[k][rem].assign(cnt + 1, 0);
}
}
auto first_ge = [](int x, int p, int rem) {
int cur = x % p;
int add = (rem - cur + p) % p;
return x + add;
};
auto last_le = [](int x, int p, int rem) {
int cur = x % p;
int sub = (cur - rem + p) % p;
return x - sub;
};
for (int qi = 0; qi < q; qi++) {
int l, r;
cin >> l >> r;
for (int k = 0; k < levels; k++) {
int low = 1 << k;
int p = 1 << (k + 1);
int rem = (l - 1 + low) % p;
int L = first_ge(l, p, rem);
int R = last_le(r, p, rem);
if (L > R) continue;
int first = (rem == 0 ? p : rem);
int leftIdx = (L - first) / p;
int rightIdx = (R - first) / p;
ll A = low;
ll B = 1LL * low * (1 - l);
da[k][rem][leftIdx] += A;
db[k][rem][leftIdx] += B;
da[k][rem][rightIdx + 1] -= A;
db[k][rem][rightIdx + 1] -= B;
}
}
vector<ll> ans(n + 1, 0);
for (int k = 0; k < levels; k++) {
int p = 1 << (k + 1);
for (int rem = 0; rem < p; rem++) {
if (da[k][rem].empty()) continue;
ll ca = 0, cb = 0;
int first = (rem == 0 ? p : rem);
int cnt = (int)da[k][rem].size() - 1;
for (int idx = 0; idx < cnt; idx++) {
ca += da[k][rem][idx];
cb += db[k][rem][idx];
int pos = first + idx * p;
ans[pos] += ca * pos + cb;
}
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << (i == n ? '\n' : ' ');
}
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
int levels = 0;
while ((1 << levels) <= n) levels++;
vector<vector<vector<ll>>> da(levels), db(levels);
for (int k = 0; k < levels; k++) {
int p = 1 << (k + 1);
da[k].resize(p);
db[k].resize(p);
for (int rem = 0; rem < p; rem++) {
int first = (rem == 0 ? p : rem);
if (first > n) continue;
int cnt = (n - first) / p + 1;
da[k][rem].assign(cnt + 1, 0);
db[k][rem].assign(cnt + 1, 0);
}
}
auto first_ge = [](int x, int p, int rem) {
int cur = x % p;
int add = (rem - cur + p) % p;
return x + add;
};
auto last_le = [](int x, int p, int rem) {
int cur = x % p;
int sub = (cur - rem + p) % p;
return x - sub;
};
for (int qi = 0; qi < q; qi++) {
int l, r;
cin >> l >> r;
for (int k = 0; k < levels; k++) {
int low = 1 << k;
int p = 1 << (k + 1);
int rem = (l - 1 + low) % p;
int L = first_ge(l, p, rem);
int R = last_le(r, p, rem);
if (L > R) continue;
int first = (rem == 0 ? p : rem);
int leftIdx = (L - first) / p;
int rightIdx = (R - first) / p;
ll A = low;
ll B = 1LL * low * (1 - l);
da[k][rem][leftIdx] += A;
db[k][rem][leftIdx] += B;
da[k][rem][rightIdx + 1] -= A;
db[k][rem][rightIdx + 1] -= B;
}
}
vector<ll> ans(n + 1, 0);
for (int k = 0; k < levels; k++) {
int p = 1 << (k + 1);
for (int rem = 0; rem < p; rem++) {
if (da[k][rem].empty()) continue;
ll ca = 0, cb = 0;
int first = (rem == 0 ? p : rem);
int cnt = (int)da[k][rem].size() - 1;
for (int idx = 0; idx < cnt; idx++) {
ca += da[k][rem][idx];
cb += db[k][rem][idx];
int pos = first + idx * p;
ans[pos] += ca * pos + cb;
}
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << (i == n ? '\n' : ' ');
}
}
}