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.
For a value , its contribution is just parity: it adds iff appears an odd number of times in the query range. The hard part is not parity itself; it is that scalar prefix beauties do not subtract cleanly. Same value on both sides will mess that up.
Translate the cute condition: there is no subsequence with . So value ranges cannot cross. They can be disjoint or nested, which is exactly the kind of structure a stack/tree likes.
Scan left to right with a stack of currently open first occurrences. Make each position a child of the stack top; push it if it is the first occurrence of its value, and pop when its value reaches the last occurrence. Cuteness guarantees the pop is legal.
In that tree, the original array is preorder. A query interval can be split around into: a suffix of one child subtree, a middle run of whole children of , and a prefix of another child subtree.
Precompute prefix/suffix beauties for partial child-subtree pieces, subtree beauties for whole child subtrees, and for every first-occurrence node a prefix table over its children that tracks both completed-child beauty and parity of the node's own value. Then each online query is only LCA plus a few table lookups.
The condition is the whole problem. If you ignore it, you get baited into Mo's algorithm, but the queries are online, so nope. If you try plain prefix beauties, that also dies because beauty is a parity set, not a normal additive sum.
So we need to use the structure forced by cute arrays.
The Tree Hidden In The Array
Cute means there is no subsequence with .
That says two values cannot have crossing occurrence ranges. If value starts, then value starts inside it, then appears again, then appears again, we have exactly the forbidden pattern. So the ranges are nested or disjoint. That screams stack.
Add a fake vertex with value . Let and be the first and last positions of value . Build a rooted tree like this:
Why is popping valid? Suppose we are at the last occurrence of , but some value opened after the first is still on the stack. Then we have
which gives the forbidden subsequence . So cute arrays make the stack behave. Nice for once.
The DFS preorder of this tree is exactly , because every new vertex is appended as the next DFS visit. So a subarray is a contiguous preorder interval in this tree.
Also, non-first occurrences are leaves. Only first occurrences get pushed, so only they can later receive children.
What We Need To Answer
For a range , find in this tree. The preorder interval splits into at most three useful parts:
The middle is the annoying part because multiple occurrences of can appear as direct children of , and their parity must be counted together. Treating those singleton children independently is the classic off-by-one-but-actually-wrong trap.
Precomputations
First compute normal prefix and suffix beauties by toggling parity:
So is the beauty of .
Next compute, for every first-occurrence vertex :
To compute , process vertices from down to . If is a first occurrence, look at its children. A child that is another first occurrence contributes its whole . A child with the same value as is just another occurrence of , so it toggles the parity bit for .
So conceptually:
The fake root has value , so its parity bit is harmless.
Prefix Tables Over Children
For each first-occurrence vertex , let its children be
Build a table over boundaries around these children. Each state stores two things:
sum: total beauty from completed child subtrees.bit: parity of occurrences of seen so far, counting itself and same-valued direct child leaves.We encode the pair as 2 * sum + bit.
Boundary meanings:
If we need the middle chunk between two boundaries and , decode both states. The answer is
That is the whole reason we store the parity bit separately. Without it, same-valued direct children would quietly break the answer.
Answering A Query
If , answer .
Otherwise compute .
For the left side:
pl = 0.pl = pos(l)+1.
because values inside that full child subtree do not occur outside it. Then set pl = pos(c)+2, meaning the middle starts after that child.
For the right side:
, so pr = 0`.pr = pos(r)+1.
Then set pr = pos(c), meaning the middle ends before that child.
Finally add the middle contribution from acc[u][pl] to acc[u][pr+1].
Finding the child of that contains a node is done by binary lifting: lift the node upward while its ancestor is still strictly below .
Correctness Sketch
The stack tree is valid because any value opened inside another value must close before the outer value closes. Otherwise we directly get , which is forbidden.
The subtree of a first-occurrence child is independent from the outside: every value that starts inside it finishes inside it, again because crossing would form . Therefore whole child subtrees can be added using sub, and partial prefixes/suffixes can be extracted using global pref/suff differences.
The only values that can appear across several direct children of a node are copies of itself. Those direct non-first children are leaves, and the accumulator table handles their parity together with . That is why the middle formula is exact.
The query decomposition covers every index in exactly once: left partial child suffix, middle whole-child run around the LCA, and right partial child prefix. Each part contributes exactly the values with odd parity inside that part, and the only cross-part parity interaction is , handled by the middle bit. So the computed value is exactly the beauty of .
Preprocessing costs for binary lifting and for all beauty tables. Each query costs . With total , this is comfortably AC.
#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;
vector<int> a(n + 1, 0), first(n + 1, 0), last(n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (first[a[i]] == 0) first[a[i]] = i;
last[a[i]] = i;
}
first[0] = last[0] = 0;
int LOG = 1;
while ((1 << LOG) <= n) LOG++;
vector<vector<int>> up(LOG, vector<int>(n + 1, 0));
vector<int> depth(n + 1, 0), childPos(n + 1, 0);
vector<vector<int>> child(n + 1);
vector<int> st;
st.push_back(0);
for (int i = 1; i <= n; i++) {
int p = st.back();
up[0][i] = p;
depth[i] = depth[p] + 1;
childPos[i] = (int) child[p].size();
child[p].push_back(i);
if (first[a[i]] == i) st.push_back(i);
if (last[a[i]] == i) st.pop_back();
}
for (int k = 1; k < LOG; k++) {
for (int v = 0; v <= n; v++) {
up[k][v] = up[k - 1][up[k - 1][v]];
}
}
vector<ll> pref(n + 1, 0), suff(n + 1, 0);
vector<int> parity(n + 1, 0);
ll cur = 0;
for (int i = 1; i <= n; i++) {
if (parity[a[i]]) cur -= a[i];
else cur += a[i];
parity[a[i]] ^= 1;
pref[i] = cur;
}
fill(parity.begin(), parity.end(), 0);
cur = 0;
for (int i = n; i >= 1; i--) {
if (parity[a[i]]) cur -= a[i];
else cur += a[i];
parity[a[i]] ^= 1;
suff[i - 1] = cur;
}
vector<ll> sub(n + 1, 0);
vector<int> lastIn(n + 1);
iota(lastIn.begin(), lastIn.end(), 0);
for (int v = n; v >= 0; v--) {
if (first[a[v]] != v) continue;
ll total = 0;
int bit = 1;
lastIn[v] = v;
for (int c : child[v]) {
total += sub[c];
if (a[c] == a[v]) bit ^= 1;
lastIn[v] = max(lastIn[v], lastIn[c]);
}
sub[v] = total + (bit ? a[v] : 0);
}
vector<vector<ll>> acc(n + 1);
for (int v = 0; v <= n; v++) {
if (first[a[v]] != v) continue;
int d = (int) child[v].size();
acc[v].assign(d + 2, 0);
ll total = 0;
int bit = 1;
acc[v][1] = 1;
for (int i = 0; i < d; i++) {
int c = child[v][i];
total += sub[c];
if (a[c] == a[v]) bit ^= 1;
acc[v][i + 2] = total * 2 + bit;
}
}
auto lca = [&](int u, int v) {
if (depth[u] < depth[v]) swap(u, v);
int diff = depth[u] - depth[v];
for (int k = 0; k < LOG; k++) {
if ((diff >> k) & 1) u = up[k][u];
}
if (u == v) return u;
for (int k = LOG - 1; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
u = up[k][u];
v = up[k][v];
}
}
return up[0][u];
};
auto childUnder = [&](int v, int anc) {
for (int k = LOG - 1; k >= 0; k--) {
if (depth[up[k][v]] > depth[anc]) v = up[k][v];
}
return v;
};
ll prev = 0;
for (int qi = 0; qi < q; qi++) {
ll x, y;
cin >> x >> y;
int l = (int) ((x - 1 + prev) % n) + 1;
int r = (int) ((y - 1 + prev) % n) + 1;
if (l > r) swap(l, r);
ll ans = 0;
if (l == r) {
ans = a[l];
} else {
int u = lca(l, r);
int pl, pr;
if (l == u && first[a[l]] == l) {
pl = 0;
} else if (up[0][l] == u && first[a[u]] == u && first[a[l]] != l) {
pl = childPos[l] + 1;
} else {
int c = childUnder(l, u);
ans += suff[l - 1] - suff[lastIn[c]];
pl = childPos[c] + 2;
}
if (r == u && first[a[r]] == r) {
pr = 0;
} else if (up[0][r] == u && first[a[u]] == u && first[a[r]] != r) {
pr = childPos[r] + 1;
} else {
int c = childUnder(r, u);
ans += pref[r] - pref[c - 1];
pr = childPos[c];
}
ll leftState = acc[u][pl];
ll rightState = acc[u][pr + 1];
ll leftSum = leftState / 2;
ll rightSum = rightState / 2;
int leftBit = (int) (leftState & 1);
int rightBit = (int) (rightState & 1);
ans += rightSum - leftSum;
if (leftBit ^ rightBit) ans += a[u];
}
if (qi) cout << ' ';
cout << ans;
prev = ans;
}
cout << char(10);
}
}#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;
vector<int> a(n + 1, 0), first(n + 1, 0), last(n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (first[a[i]] == 0) first[a[i]] = i;
last[a[i]] = i;
}
first[0] = last[0] = 0;
int LOG = 1;
while ((1 << LOG) <= n) LOG++;
vector<vector<int>> up(LOG, vector<int>(n + 1, 0));
vector<int> depth(n + 1, 0), childPos(n + 1, 0);
vector<vector<int>> child(n + 1);
vector<int> st;
st.push_back(0);
for (int i = 1; i <= n; i++) {
int p = st.back();
up[0][i] = p;
depth[i] = depth[p] + 1;
childPos[i] = (int) child[p].size();
child[p].push_back(i);
if (first[a[i]] == i) st.push_back(i);
if (last[a[i]] == i) st.pop_back();
}
for (int k = 1; k < LOG; k++) {
for (int v = 0; v <= n; v++) {
up[k][v] = up[k - 1][up[k - 1][v]];
}
}
vector<ll> pref(n + 1, 0), suff(n + 1, 0);
vector<int> parity(n + 1, 0);
ll cur = 0;
for (int i = 1; i <= n; i++) {
if (parity[a[i]]) cur -= a[i];
else cur += a[i];
parity[a[i]] ^= 1;
pref[i] = cur;
}
fill(parity.begin(), parity.end(), 0);
cur = 0;
for (int i = n; i >= 1; i--) {
if (parity[a[i]]) cur -= a[i];
else cur += a[i];
parity[a[i]] ^= 1;
suff[i - 1] = cur;
}
vector<ll> sub(n + 1, 0);
vector<int> lastIn(n + 1);
iota(lastIn.begin(), lastIn.end(), 0);
for (int v = n; v >= 0; v--) {
if (first[a[v]] != v) continue;
ll total = 0;
int bit = 1;
lastIn[v] = v;
for (int c : child[v]) {
total += sub[c];
if (a[c] == a[v]) bit ^= 1;
lastIn[v] = max(lastIn[v], lastIn[c]);
}
sub[v] = total + (bit ? a[v] : 0);
}
vector<vector<ll>> acc(n + 1);
for (int v = 0; v <= n; v++) {
if (first[a[v]] != v) continue;
int d = (int) child[v].size();
acc[v].assign(d + 2, 0);
ll total = 0;
int bit = 1;
acc[v][1] = 1;
for (int i = 0; i < d; i++) {
int c = child[v][i];
total += sub[c];
if (a[c] == a[v]) bit ^= 1;
acc[v][i + 2] = total * 2 + bit;
}
}
auto lca = [&](int u, int v) {
if (depth[u] < depth[v]) swap(u, v);
int diff = depth[u] - depth[v];
for (int k = 0; k < LOG; k++) {
if ((diff >> k) & 1) u = up[k][u];
}
if (u == v) return u;
for (int k = LOG - 1; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
u = up[k][u];
v = up[k][v];
}
}
return up[0][u];
};
auto childUnder = [&](int v, int anc) {
for (int k = LOG - 1; k >= 0; k--) {
if (depth[up[k][v]] > depth[anc]) v = up[k][v];
}
return v;
};
ll prev = 0;
for (int qi = 0; qi < q; qi++) {
ll x, y;
cin >> x >> y;
int l = (int) ((x - 1 + prev) % n) + 1;
int r = (int) ((y - 1 + prev) % n) + 1;
if (l > r) swap(l, r);
ll ans = 0;
if (l == r) {
ans = a[l];
} else {
int u = lca(l, r);
int pl, pr;
if (l == u && first[a[l]] == l) {
pl = 0;
} else if (up[0][l] == u && first[a[u]] == u && first[a[l]] != l) {
pl = childPos[l] + 1;
} else {
int c = childUnder(l, u);
ans += suff[l - 1] - suff[lastIn[c]];
pl = childPos[c] + 2;
}
if (r == u && first[a[r]] == r) {
pr = 0;
} else if (up[0][r] == u && first[a[u]] == u && first[a[r]] != r) {
pr = childPos[r] + 1;
} else {
int c = childUnder(r, u);
ans += pref[r] - pref[c - 1];
pr = childPos[c];
}
ll leftState = acc[u][pl];
ll rightState = acc[u][pr + 1];
ll leftSum = leftState / 2;
ll rightSum = rightState / 2;
int leftBit = (int) (leftState & 1);
int rightBit = (int) (rightState & 1);
ans += rightSum - leftSum;
if (leftBit ^ rightBit) ans += a[u];
}
if (qi) cout << ' ';
cout << ans;
prev = ans;
}
cout << char(10);
}
}