G - Smaller Sum
G - Smaller Sum
Problem Statement
You are given a sequence of length .
Answer the following queries. The -th query is as follows:
- Find the sum of the elements among that are not greater than .
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 -th query itself, you are given encrypted inputs for the query. Restore the original -th query using the following steps and then answer it.
- Let and (the answer to the -th query).
- Then, the query can be decrypted as follows:
Here, denotes the bitwise XOR of and .
What is bitwise XOR?
The bitwise XOR of non-negative integers and , , is defined as follows:- The digit in the place () of in binary is if exactly one of the corresponding digits of and in binary is , and otherwise.
Constraints
- All input values are integers.
- For the encrypted inputs, the following holds:
- For the decrypted queries, the following holds:
Input
The input is given from Standard Input in the following format:
Output
Print lines.
The -th line should contain the answer to the -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 .
This input contains five queries.
- Initially, .
- The first query is .
- After decryption, we get .
- The answer to this query is . This becomes .
- The next query is .
- After decryption, we get .
- The answer to this query is . This becomes .
- The next query is .
- After decryption, we get .
- The answer to this query is . This becomes .
- The next query is .
- After decryption, we get .
- The answer to this query is . This becomes .
- The next query is .
- After decryption, we get .
- The answer to this query is . This becomes .
解题思路
题目大意就是每次询问区间 内不超过 的 的总和,并且由于输入的特殊性导致强行在线处理。
可以用可持久化线段树来做。具体来说,这里的线段树是权值线段树,线段树节点维护的是对应值域内出现的数的总和,记作 。然后维护出 个版本的线段树,其中第 个版本的线段树是指插入 后的线段树。那么对于询问 ,只需对第 个和第 个版本的线段树求 范围内 的值即可,这里的 指第 个和第 个版本的线段树中每个节点的 的差值,表示的只考虑在 中某个值域范围内出现的数的总和。
AC 代码如下,时间复杂度为 :
#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 更新了分块的做法。
最近学了分块,回想起之前看题解时记得这题可以用分块来做,试了下还真能跑。不过最久的样例跑了 ,这题的时限为 ,差点就 TLE 了。
对数组 进行分块,每块的大小为 。用 表示第 个元素在哪个分块, 和 分别表示第 个分块的左端点和右端点。对每个分块内的元素进行排序,并存储到数组 对应的分块中,同时求出数组 的前缀和 。
所以对于每个询问 ,如果 和 在同一个分块内,则直接暴力统计 中值不超过 的元素和。否则,先暴力统计 中值不超过 的元素和,以及 中值不超过 的元素和。最后统计第 到第 的每个分块内,值不超过 的元素和。对于第 个分块,只需在数组 中对应的分块内二分出不超过 的最大下标 ,那么该分块内值不超过 的元素数量和就是 。
AC 代码如下,时间复杂度为 :
#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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-02-04 G2. Teleporters (Hard Version)
2023-02-04 E. Negatives and Positives