G - Smaller Sum

G - Smaller Sum

Problem Statement

You are given a sequence A=(A1,A2,,AN) of length N.

Answer the following Q queries. The i-th query is as follows:

  • Find the sum of the elements among ALi,ALi+1,,ARi that are not greater than Xi.

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 αi,βi,γi for the query. Restore the original i-th query using the following steps and then answer it.

  • Let B0=0 and Bi= (the answer to the i-th query).
  • Then, the query can be decrypted as follows:
    • Li=αiBi1
    • Ri=βiBi1
    • Xi=γiBi1

Here, xy denotes the bitwise XOR of x and y.

What is bitwise XOR? The bitwise XOR of non-negative integers A and B, AB, is defined as follows:
  • The digit in the 2k place (k0) of AB in binary is 1 if exactly one of the corresponding digits of A and B in binary is 1, and 0 otherwise.
For example, 35=6 (in binary: 011101=110).

Constraints

  • All input values are integers.
  • 1N2×105
  • 0Ai109
  • 1Q2×105
  • For the encrypted inputs, the following holds:
    • 0αi,βi,γi1018
  • For the decrypted queries, the following holds:
    • 1LiRiN
    • 0Xi109

Input

The input is given from Standard Input in the following format:

N
A1 A2 AN
Q
α1 β1 γ1
α2 β2 γ2

αQ βQ γ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, B0=0.
  • The first query is α=1,β=8,γ=3.
    • After decryption, we get Li=αB0=1,Ri=βB0=8,Xi=γB0=3.
    • The answer to this query is 9. This becomes B1.
  • The next query is α=10,β=12,γ=11.
    • After decryption, we get Li=αB1=3,Ri=βB1=5,Xi=γB1=2.
    • The answer to this query is 2. This becomes B2.
  • The next query is α=3,β=3,γ=2.
    • After decryption, we get Li=αB2=1,Ri=βB2=1,Xi=γB2=0.
    • The answer to this query is 0. This becomes B3.
  • The next query is α=3,β=6,γ=5.
    • After decryption, we get Li=αB3=3,Ri=βB3=6,Xi=γB3=5.
    • The answer to this query is 8. This becomes B4.
  • The next query is α=12,β=0,γ=11.
    • After decryption, we get Li=αB4=4,Ri=βB4=8,Xi=γB4=3.
    • The answer to this query is 5. This becomes B5.

 

解题思路

  题目大意就是每次询问区间 i[l,r] 内不超过 xai 的总和,并且由于输入的特殊性导致强行在线处理。

  可以用可持久化线段树来做。具体来说,这里的线段树是权值线段树,线段树节点维护的是对应值域内出现的数的总和,记作 s。然后维护出 n 个版本的线段树,其中第 i 个版本的线段树是指插入 a1ai 后的线段树。那么对于询问 (l,r,x),只需对第 r 个和第 l1 个版本的线段树求 [0,x] 范围内 s 的值即可,这里的 s 指第 r 个和第 l1 个版本的线段树中每个节点的 s 的差值,表示的只考虑在 alar 中某个值域范围内出现的数的总和。

  AC 代码如下,时间复杂度为 O(nlogA+qlogA)

#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 ms,这题的时限为 3.5 s,差点就 TLE 了。

  对数组 a 进行分块,每块的大小为 len=n。用 idi 表示第 i 个元素在哪个分块,LiRi 分别表示第 i 个分块的左端点和右端点。对每个分块内的元素进行排序,并存储到数组 b 对应的分块中,同时求出数组 b 的前缀和 s

  所以对于每个询问 (l,r,x),如果 lr 在同一个分块内,则直接暴力统计 alar 中值不超过 x 的元素和。否则,先暴力统计 alaRidl 中值不超过 x 的元素和,以及 aLidrar 中值不超过 x 的元素和。最后统计第 idl+1 到第 idr1 的每个分块内,值不超过 x 的元素和。对于第 i 个分块,只需在数组 b 中对应的分块内二分出不超过 x 的最大下标 t,那么该分块内值不超过 x 的元素数量和就是 stsLi1

  AC 代码如下,时间复杂度为 O(qnlogn)

#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 @   onlyblues  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2023-02-04 G2. Teleporters (Hard Version)
2023-02-04 E. Negatives and Positives
Web Analytics
点击右上角即可分享
微信分享提示