非常好线段树。加 ※ 的是开始不会,讲了之后才会的题。
CSES 1648- Dynamic Range Sum Queries
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 查询有两种类型:
1 k u:将位置 \(k\) 的值更新为 \(u\)2 a b:查询区间 \([a, b]\) 的元素和- 对于每个类型 2 的查询,输出对应的区间和
数据范围:
- \(1 \le n,\, q \le 2 \times 10^{5}\)
- \(1 \le x_i,\, u \le 10^{9}\)
- \(1 \le k \le n\)
- \(1 \le a \le b \le n\)
单点改区间求和。
CSES 1649- Dynamic Range Minimum Queries
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 查询有两种类型:
1 k u:将位置 \(k\) 的值更新为 \(u\)2 a b:查询区间 \([a, b]\) 的最小值- 对于每个类型 2 的查询,输出对应的区间最小值
数据范围:
- \(1 \le n, q \le 2 \times 10^{5}\)
- \(1 \le x_i, u \le 10^{9}\)
- \(1 \le k \le n\)
- \(1 \le a \le b \le n\)
单点改区间求最小值。
CSES 1650- Range Xor Queries
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 每次查询给出区间 \([a, b]\),要求计算该区间内所有值的异或和
- 异或和:将区间内所有元素按位进行异或 (\(\text{XOR}\)) 操作的结果
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
由于没有修改直接预处理前缀异或和即可。
CSES 1651- Range Update Queries
给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询。
查询有两种类型:
- (1) \(1\ a\ b\ u\): 将区间 \([a, b]\) 内的每个值增加 \(u\)
- (2) \(2\ k\): 查询位置 \(k\) 的当前值
对于每个类型 2 的查询,输出对应位置的值
\(1 \leq n, q \leq 2 \times 10^5\)
\(1 \leq x_i, u \leq 10^9\)
\(1 \leq k \leq n\)
\(1 \leq a \leq b \leq n\)
区间加单点查询,直接差分后 BIT 或线段树。
CSES 1652- Forest Queries
给定一个 \(n \times n\) 的网格森林,每个格子是空地 (\(.\)) 或树 (\(*\))
需要处理 \(q\) 次矩形查询
每次查询给出矩形区域 \((y_1, x_1)\) 到 \((y_2, x_2)\),要求统计该区域内树的数量
坐标系统:左上角为 \((1, 1)\),右下角为 \((n, n)\)
\(1 \le n \le 1000\)
\(1 \le q \le 2 \times 10^5\)
\(1 \le y_1 \le y_2 \le n\)
\(1 \le x_1 \le x_2 \le n\)
二维前缀和。
CSES 1143- Hotel Queries
- 有 \(n\) 家酒店排成一列,每家酒店有一定数量的空房间
- 有 \(m\) 个旅游团依次前来订房,每个团需要一定数量的房间
- 分配规则:
(1) 每个团的所有成员必须住在同一家酒店
(2) 按顺序选择第一家有空房的酒店(房间数 \(\ge\) 需求数)
(3) 分配后该酒店的空房间数相应减少- 对于每个团,输出被分配的酒店编号,若无法分配则输出 0
数据范围:
- \(1 \le n, m \le 2 \times 10^5\)
- \(1 \le h_i \le 10^9\)
- \(1 \le r_i \le 10^9\)
相当于每次查第一个 \(\ge r_i\) 的位置,然后单点修改。直接线段树上二分。
※ CSES 1749- List Removals
- 给定一个包含 \(n\) 个整数的列表,需要按顺序执行 \(n\) 次删除操作
- 每次删除操作给出一个位置 \(p_i\),表示要删除当前列表中第 \(p_i\) 个元素
- 删除后列表长度减少,后续元素的位置会前移
- 要求按删除顺序输出所有被删除的元素值
数据范围:
- \(1 \le n \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le p_i \le n-i+1\) (保证位置有效)
使用 pbds 的 ordered_set。
CSES 1144- Salary Queries
- 公司有 \(n\) 名员工,每人有一个工资值
- 需要处理 \(q\) 次查询,查询有两种类型:
(1) \(! \ k \ x\): 将员工 \(k\) 的工资改为 \(x\)
(2) \(? \ a \ b\): 统计工资在区间 \([a, b]\) 内的员工数量- 对于每个 \(?\) 查询,输出满足条件的员工人数
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le p_i, x \le 10^9\)
- \(1 \le k \le n\)
- \(1 \le a \le b \le 10^9\)
在值域上建立一个动态开点线段树即可。
CSES 2166- Prefix Sum Queries
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 查询有两种类型:
(1) \(1 \ k \ u\): 将位置 \(k\) 的值更新为 \(u\)
(2) \(2 \ a \ b\): 查询区间 \([a, b]\) 的最大前缀和(允许空前缀,即前缀和为 0)- 对于每个类型 2 的查询,输出对应的最大前缀和
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(-10^9 \le x_i, u \le 10^9\)
- \(1 \le k \le n\)
- \(1 \le a \le b \le n\)
一个节点的最大前缀和 = max(左儿子最大前缀和, 左儿子的总和+右儿子的最大前缀和)
※ CSES 2206- Pizzeria Queries
- 一条街上有 \(n\) 栋建筑,每栋建筑有一个披萨店
- 从建筑 \(a\) 订披萨送到建筑 \(b\) 的总价格为:\(p_a + |a - b|\)
- 需要处理 \(q\) 次查询,查询有两种类型:
(1) \(1 \ k \ x\): 将建筑 \(k\) 的披萨价格改为 \(x\)
(2) \(2 \ k\): 查询当前在建筑 \(k\) 时,订披萨的最低总价格- 对于每个类型 2 的查询,输出最小价格
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le p_i, x \le 10^9\)
- \(1 \le k \le n\)
考虑拆绝对值,将对于 \(a<k\) 的相当于在问 \(p_a-a\) 的最大值,对于 \(a>k\) 的求 \(p_a+a\) 的最大值。
CSES 3304- Visible Buildings Queries
- 有 \(n\) 栋建筑排成一行,每栋建筑有一个高度
- 从最左边观察,能看到一栋建筑当且仅当它比左边所有建筑都高
- 需要处理 \(q\) 次查询:如果只考虑区间 \([a, b]\) 内的建筑,能看到多少栋?
数据范围:
- \(1 \le n \le 10^5\)
- \(1 \le q \le 2 \times 10^5\)
- \(1 \le h_i \le 10^9\)
- \(1 \le a \le b \le n\)
相当于在问区间 \([a,b]\) 里有多少个前缀最大值。
对 \(a\) 扫描线(从右往左扫),维护一个单调栈表示前缀最大值,在单调栈上二分 \(\le b\) 有多少个即可。
CSES 3163- Range Interval Queries
- 给一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 每次查询给出四个整数 \(a, b, c, d\)
- 查询目标:统计满足以下两个条件的下标 \(i\) 的数量
(1) 下标在区间 \([a, b]\) 内
(2) 数组元素值在区间 \([c, d]\) 内数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
- \(1 \le c \le d \le 10^9\)
差分之后是二维数点。
CSES 1190- Subarray Sum Queries
- 给一个包含 \(n\) 个整数的数组,需要处理 \(m\) 次更新操作
- 每次更新将位置 \(k\) 的值改为 \(x\)
- 每次更新后,需要计算并输出当前数组的最大子数组和
- 允许空子数组(和为 \(0\))
数据范围:
- \(1 \le n, m \le 2 \times 10^5\)
- \(-10^9 \le x_i, x \le 10^9\)
- \(1 \le k \le n\)
小白逛公园弱化版。
CSES 3226- Subarray Sum Queries II
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次区间查询
- 每次查询给出区间 \([a, b]\),要求计算该区间内的最大子数组和
- 允许空子数组(和为 \(0\))
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(-10^9 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
小白逛公园。
※ CSES 1734- Distinct Values Queries
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次区间查询
- 每次查询给出区间 \([a, b]\),要求统计该区间内不同数值的个数
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
离线所有询问,对 \(b\) 扫描线,扫描线了之后发现,只要对于每一个数,把他最后一次出现赋成 \(1\),其他位置赋成 \(0\),相当于就是区间查询。
const int MAXN = 2e5 + 5;
int n, q, a[MAXN], ans[MAXN];map<int, int> lst;struct _query {int l, r, id;bool operator < (const _query b) const {return r < b.r;}
} qr[MAXN];struct _bit {int tr[MAXN];int lowbit(int x) { return x & (-x); }int query(int x) {if (x <= 0) return 0;int ret = 0;while (x) {ret += tr[x];x -= lowbit(x);}return ret;}void modify(int x, int v) {while (x <= n) {tr[x] += v;x += lowbit(x);}}
} bit;void work() {cin >> n >> q;for (int i = 1; i <= n; ++i) {cin >> a[i];}for (int i = 1; i <= q; ++i) {cin >> qr[i].l >> qr[i].r;qr[i].id = i;}sort(qr + 1, qr + 1 + q);int j = 1;for (int i = 1; i <= n; ++i) {if (lst[a[i]]) {bit.modify(lst[a[i]], -1);}bit.modify(i, 1);lst[a[i]] = i;while (j <= q && qr[j].r < i) ++j;while (j <= q && qr[j].r == i) {ans[qr[j].id] = bit.query(n) - bit.query(qr[j].l - 1);++j;}}for (int i = 1; i <= q; ++i)cout << ans[i] << endl;
}
※ CSES 3356- Distinct Values Queries II
- 给定一个包含 \(n\) 个整数的数组,需要处理 \(q\) 次查询
- 查询有两种类型:
(1) \(1 \ k \ u\): 将位置 \(k\) 的值更新为 \(u\)
(2) \(2 \ a \ b\): 检查区间 \([a, b]\) 内的所有值是否互不相同- 对于每个类型 \(2\) 的查询,如果区间内所有值互不相同则输出 \(\text{YES}\),否则输出 \(\text{NO}\)
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i, u \le 10^9\)
- \(1 \le k \le n\)
- \(1 \le a \le b \le n\)
这个套路见了好多遍还是不会。设 \(lst_i\) 表示 \(a_i\) 上一次出现的位置,则区间内所有值互不相同等价于 \(\max lst_i < l\)。然后每次修改只会影响 \(O(1)\) 个 \(lst\)。
CSES 2416- Increasing Array Queries
- 给定一个包含 \(n\) 个整数的数组,每次操作可以将一个元素的值增加 \(1\)
- 需要处理 \(q\) 次区间查询:对于区间 \([a, b]\),最少需要多少次操作能使该区间变为非递减序列?
- 非递减序列:每个元素 \(\ge\) 前一个元素
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
考虑相当于把区间里的每一个元素改为他前面的前缀 \(\max\)。于是还是把所有询问离线下来,对 \(a\) 从右往左扫描线,维护一个单调栈表示前缀最大值。每次新增一个值的时候,弹栈的过程中算一下贡献的变化即可。
※ CSES 1664- Movie Festival Queries
- 有 \(n\) 部电影,每部电影有开始和结束时间
- 需要处理 \(q\) 次查询:在指定到达时间 \(a\) 和离开时间 \(b\) 内,最多能看多少部电影?
- 观影规则:
(1) 可以连续观看多部电影,但下一部必须在前一部结束后(或同时)开始
(2) 可以在到达时立即开始观看第一部电影
(3) 可以在最后一部电影结束时立即离开数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le a < b \le 10^6\)
最优策略显然是在所有能看的电影中选择结束时间最早的电影。
因为没有修改操作,当看完一个电影 \(i\) 之后,下一个看的电影 \(j\) 一定是固定的,不妨设为 \(nxt_i=j\),然后用倍增维护即可,设 \(f_{i,k}\) 表示从 \(i\) 开始看 \(2^k\) 个电影的最早结束时间,\(g_{i,k}\) 表示从 \(i\) 开始看 \(2^k\) 个电影后的下一个电影是谁。
CSES 1739- Forest Queries II
- 给定一个 \(n \times n\) 的网格森林,每个格子是空地 \((\text{.})\) 或树 \((\ast)\)
- 需要处理 \(q\) 次查询,查询有两种类型:
(1) \(1 \ y \ x\): 切换位置 \((y, x)\) 的状态(空地变树,树变空地)
(2) \(2 \ y1 \ x1 \ y2 \ x2\): 统计矩形区域 \((y1, x1)\) 到 \((y2, x2)\) 内的树的数量- 对于每个类型 \(2\) 的查询,输出对应矩形区域内树的数量
数据范围:
- \(1 \le n \le 1000\)
- \(1 \le q \le 2 \times 10^5\)
- \(1 \le y, x \le n\)
- \(1 \le y_1 \le y_2 \le n\)
- \(1 \le x_1 \le x_2 \le n\)
二维树状数组。
CSES 1735- Range Updates and Sums
- 维护一个包含 \(n\) 个值的数组,需要处理 \(q\) 次查询
- 查询有三种类型:
(1) \(1 \ a \ b \ x\): 将区间 \([a, b]\) 内的每个值增加 \(x\)
(2) \(2 \ a \ b \ x\): 将区间 \([a, b]\) 内的每个值设置为 \(x\)
(3) \(3 \ a \ b\): 计算区间 \([a, b]\) 内所有值的和- 对于每个类型 \(3\) 的查询,输出区间和
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le t_i, x \le 10^6\)
- \(1 \le a \le b \le n\)
扶苏的问题。
CSES 1736- Polynomial Queries
- 维护一个包含 \(n\) 个值的数组,需要处理 \(q\) 次查询
- 查询有两种类型:
- (1) \(1\) \(a\) \(b\): 对区间 \([a, b]\) 进行特殊增加操作 - 第 \(1\) 个元素加 \(1\),第 \(2\) 个元素加 \(2\),第 \(3\) 个元素加 \(3\),依此类推
- (2) \(2\) \(a\) \(b\): 计算区间 \([a, b]\) 内所有值的和
- 对于每个类型 \(2\) 的查询,输出区间和
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le t_i \le 10^6\)
- \(1 \le a \le b \le n\)
lazytag 维护两个信息:首项 \(f\) 和公差 \(d\)。
两个等差序列合并 \(f'=f_1+f_2,d'=d_1+d_2\)。
CSES 1737- Range Queries and Copies
- 维护一个数组列表,初始时只有一个数组
- 需要处理 \(q\) 次查询,查询有三种类型:
- (1) \(1\) \(k\) \(a\) \(x\): 将数组 \(k\) 中位置 \(a\) 的值设为 \(x\)
- (2) \(2\) \(k\) \(a\) \(b\): 计算数组 \(k\) 中区间 \([a, b]\) 的和
- (3) \(3\) \(k\): 创建数组 \(k\) 的副本,并添加到列表末尾
- 对于每个类型 \(2\) 的查询,输出对应的区间和
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le t_i, x \le 10^9\)
- \(1 \le a \le b \le n\)
可持久化线段树。
※ CSES 2184- Missing Coin Sum Queries
- 有 \(n\) 枚硬币,每枚硬币有一个正整数值。这些硬币被编号为 \(1, 2, \dots, n\)。
- 需要处理 \(q\) 次区间查询: 如果只能使用区间 \([a, b]\) 内的硬币,无法凑出的最小正整数是多少?
数据范围:
- \(1 \le n, q \le 2 \times 10^5\)
- \(1 \le x_i \le 10^9\)
- \(1 \le a \le b \le n\)
设我们现在用了面值在 \([1,x]\) 区间里的硬币,能表示出 \([1,s]\) 里的所有数,我们想要表示 \(s+1\),我们就可以用 \([x+1,s]\) 里的所有数,最后能表示的数就变成了 \([1,s+sum]\),其中 \(sum\) 是 \([x+1,s]\) 里的所有数之和,\(x\) 变为现在的 \(s\),由于每次你的 \(s\) 都至少翻倍,所以这个一次操作是 \(O(\log n)\) 的。