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.
For example, $3 \oplus 5 = 6$ (in binary: $011 \oplus 101 = 110$).

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

posted @ 2024-02-04 20:31  onlyblues  阅读(13)  评论(0编辑  收藏  举报
Web Analytics