G - Smaller Sum
G - Smaller Sum
Problem Statement
You are given a sequence $A=(A_1,A_2,\dots,A_N)$ of length $N$.
Answer the following $Q$ queries. The $i$-th query is as follows:
- Find the sum of the elements among $A_{L_i},A_{L_i+1},\dots,A_{R_i}$ that are not greater than $X_i$.
Here, you need to answer these queries online.
That is, only after you answer the current query is the next query revealed.
For this reason, instead of the $i$-th query itself, you are given encrypted inputs $\alpha_i, \beta_i, \gamma_i$ for the query. Restore the original $i$-th query using the following steps and then answer it.
- Let $B_0=0$ and $B_i =$ (the answer to the $i$-th query).
- Then, the query can be decrypted as follows:
- $L_i = \alpha_i \oplus B_{i-1}$
- $R_i = \beta_i \oplus B_{i-1}$
- $X_i = \gamma_i \oplus B_{i-1}$
Here, $x \oplus y$ denotes the bitwise XOR of $x$ and $y$.
What is bitwise XOR?
The bitwise XOR of non-negative integers $A$ and $B$, $A \oplus B$, is defined as follows:- The digit in the $2^k$ place ($k \geq 0$) of $A \oplus B$ in binary is $1$ if exactly one of the corresponding digits of $A$ and $B$ in binary is $1$, and $0$ otherwise.
Constraints
- All input values are integers.
- $1 \le N \le 2 \times 10^5$
- $0 \le A_i \le 10^9$
- $1 \le Q \le 2 \times 10^5$
- For the encrypted inputs, the following holds:
- $0 \le \alpha_i, \beta_i, \gamma_i \le 10^{18}$
- For the decrypted queries, the following holds:
- $1 \le L_i \le R_i \le N$
- $0 \le X_i \le 10^9$
Input
The input is given from Standard Input in the following format:
$N$
$A_1$ $A_2$ $\dots$ $A_N$
$Q$
$\alpha_1$ $\beta_1$ $\gamma_1$
$\alpha_2$ $\beta_2$ $\gamma_2$
$\vdots$
$\alpha_Q$ $\beta_Q$ $\gamma_Q$
Output
Print $Q$ lines.
The $i$-th line should contain the answer to the $i$-th query.
Sample Input 1
8
2 0 2 4 0 2 0 3
5
1 8 3
10 12 11
3 3 2
3 6 5
12 0 11
Sample Output 1
9
2
0
8
5
The given sequence is $A=(2,0,2,4,0,2,0,3)$.
This input contains five queries.
- Initially, $B_0=0$.
- The first query is $\alpha = 1, \beta = 8, \gamma = 3$.
- After decryption, we get $L_i = \alpha \oplus B_0 = 1, R_i = \beta \oplus B_0 = 8, X_i = \gamma \oplus B_0 = 3$.
- The answer to this query is $9$. This becomes $B_1$.
- The next query is $\alpha = 10, \beta = 12, \gamma = 11$.
- After decryption, we get $L_i = \alpha \oplus B_1 = 3, R_i = \beta \oplus B_1 = 5, X_i = \gamma \oplus B_1 = 2$.
- The answer to this query is $2$. This becomes $B_2$.
- The next query is $\alpha = 3, \beta = 3, \gamma = 2$.
- After decryption, we get $L_i = \alpha \oplus B_2 = 1, R_i = \beta \oplus B_2 = 1, X_i = \gamma \oplus B_2 = 0$.
- The answer to this query is $0$. This becomes $B_3$.
- The next query is $\alpha = 3, \beta = 6, \gamma = 5$.
- After decryption, we get $L_i = \alpha \oplus B_3 = 3, R_i = \beta \oplus B_3 = 6, X_i = \gamma \oplus B_3 = 5$.
- The answer to this query is $8$. This becomes $B_4$.
- The next query is $\alpha = 12, \beta = 0, \gamma = 11$.
- After decryption, we get $L_i = \alpha \oplus B_4 = 4, R_i = \beta \oplus B_4 = 8, X_i = \gamma \oplus B_4 = 3$.
- The answer to this query is $5$. This becomes $B_5$.
解题思路
题目大意就是每次询问区间 $i \in [l, r]$ 内不超过 $x$ 的 $a_i$ 的总和,并且由于输入的特殊性导致强行在线处理。
可以用可持久化线段树来做。具体来说,这里的线段树是权值线段树,线段树节点维护的是对应值域内出现的数的总和,记作 $s$。然后维护出 $n$ 个版本的线段树,其中第 $i$ 个版本的线段树是指插入 $a_1 \sim a_i$ 后的线段树。那么对于询问 $(l,r,x)$,只需对第 $r$ 个和第 $l-1$ 个版本的线段树求 $[0, x]$ 范围内 $s$ 的值即可,这里的 $s$ 指第 $r$ 个和第 $l-1$ 个版本的线段树中每个节点的 $s$ 的差值,表示的只考虑在 $a_l \sim a_r$ 中某个值域范围内出现的数的总和。
AC 代码如下,时间复杂度为 $O(n \log{A} + q \log{A})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int a[N];
struct Node {
int l, r;
LL s;
}tr[N * 35];
int root[N], idx;
int modify(int u, int l, int r, int x, int c) {
int v = ++idx;
tr[v] = tr[u];
if (l == r) {
tr[v].s += c;
return v;
}
int mid = l + r >> 1;
if (x <= mid) tr[v].l = modify(tr[u].l, l, mid, x, c);
else tr[v].r = modify(tr[u].r, mid + 1, r, x, c);
tr[v].s = tr[tr[v].l].s + tr[tr[v].r].s;
return v;
}
LL query(int u, int v, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return tr[u].s - tr[v].s;
int mid = l + r >> 1;
LL ret = 0;
if (ql <= mid) ret = query(tr[u].l, tr[v].l, l, mid, ql, qr);
if (qr >= mid + 1) ret += query(tr[u].r, tr[v].r, mid + 1, r, ql, qr);
return ret;
}
int main() {
int n, m, mx = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
mx = max(mx, a[i]);
}
for (int i = 1; i <= n; i++) {
root[i] = modify(root[i - 1], 0, mx, a[i], a[i]);
}
scanf("%d", &m);
LL t = 0;
while (m--) {
LL x, y, z;
scanf("%lld %lld %lld", &x, &y, &z);
int l = x ^ t, r = y ^ t, c = z ^ t;
t = query(root[r], root[l - 1], 0, mx, 0, c);
printf("%lld\n", t);
}
return 0;
}
2024-04-10 更新了分块的做法。
最近学了分块,回想起之前看题解时记得这题可以用分块来做,试了下还真能跑。不过最久的样例跑了 $3335\text{ ms}$,这题的时限为 $3.5\text{ s}$,差点就 TLE 了。
对数组 $a$ 进行分块,每块的大小为 $\text{len} = \left\lfloor\sqrt{n}\right\rfloor$。用 $\text{id}_i$ 表示第 $i$ 个元素在哪个分块,$L_i$ 和 $R_i$ 分别表示第 $i$ 个分块的左端点和右端点。对每个分块内的元素进行排序,并存储到数组 $b$ 对应的分块中,同时求出数组 $b$ 的前缀和 $s$。
所以对于每个询问 $(l,r,x)$,如果 $l$ 和 $r$ 在同一个分块内,则直接暴力统计 $a_l \sim a_r$ 中值不超过 $x$ 的元素和。否则,先暴力统计 $a_{l} \sim a_{R_{\text{id}_{l}}}$ 中值不超过 $x$ 的元素和,以及 $a_{L_{\text{id}_{r}}} \sim a_r$ 中值不超过 $x$ 的元素和。最后统计第 $\text{id}_l + 1$ 到第 $\text{id}_r - 1$ 的每个分块内,值不超过 $x$ 的元素和。对于第 $i$ 个分块,只需在数组 $b$ 中对应的分块内二分出不超过 $x$ 的最大下标 $t$,那么该分块内值不超过 $x$ 的元素数量和就是 $s_t - s_{L_{i} - 1}$。
AC 代码如下,时间复杂度为 $O(q \sqrt{n} \log{\sqrt{n}})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 455;
int a[N], b[N];
int id[N], len;
int l[M], r[M];
LL s[N];
LL query(int x, int y, int c) {
LL ret = 0;
if (id[x] == id[y]) {
for (int i = x; i <= y; i++) {
if (a[i] <= c) ret += a[i];
}
}
else {
for (int i = x; id[i] == id[x]; i++) {
if (a[i] <= c) ret += a[i];
}
for (int i = y; id[i] == id[y]; i--) {
if (a[i] <= c) ret += a[i];
}
for (int i = id[x] + 1; i < id[y]; i++) {
int t = upper_bound(b + l[i], b + r[i] + 1, c) - b - 1;
ret += s[t] - s[l[i] - 1];
}
}
return ret;
}
int main() {
int n, m;
scanf("%d", &n);
len = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
b[i] = a[i];
id[i] = (i - 1) / len + 1;
}
for (int i = 1, j = 1; i <= n; i += len, j++) {
l[j] = i, r[j] = min(n, i + len - 1);
sort(b + l[j], b + r[j] + 1);
for (int i = l[j]; i <= r[j]; i++) {
s[i] = s[i - 1] + b[i];
}
}
scanf("%d", &m);
LL t = 0;
while (m--) {
LL x, y, z;
scanf("%lld %lld %lld", &x, &y, &z);
int l = x ^ t, r = y ^ t, c = z ^ t;
t = query(l, r, c);
printf("%lld\n", t);
}
return 0;
}
参考资料
AtCoder Beginner Contest 339:https://www.cnblogs.com/Lanly/p/18005285
AtCoder Beginner Contest 339 - 2huk - 博客园:https://www.cnblogs.com/2huk/p/18005328
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18006944