qbxt 数据结构 (lxl)
又是lxl..(太好了,重复的题直接跳过了= =)
T1
给定一个序列,查询区间小于等于 \(k\) 的元素的个数。
solution
每个位置开个 \(vector\) 把询问两个端点离线下来。
然后维护一个值域树状数组,每个询问差分一下就好了。
T2
给定一个二维平面,上面有 \(n\) 个矩形,每个矩形的坐标为 \([1, n]\)
有 \(m\) 次查询,每次查询一个二维平面上的点被多少矩形覆盖。
solution
扫描线。
把询问离线下来,按照 \(x\) 排序。
然后以 \(y\) 轴为下标建一棵线段树,然后一条扫描线从左到右扫,如果扫到一个矩形的左端点就区间加,如果扫到一个矩形的右端点就区间减,查询就是单点查询了。
T3
solution
把询问离线下来,按照右端点排序。
对于每个询问,如果有颜色出现了多次,那么我们只关心它出现的最后依次就好了。
记录每个颜色上次出现的位置,然后开个树状数组,记录颜色的数量,对询问差分一下就好了。
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
map<int, int>last;
int n, tree[N], a[N], m, Ans[N], p = 1;
struct Node{int l, r, id;}A[N];
void Insert(int x, int k) {
for (int i = x; i <= n; i += i & (-i)) tree[i] += k;
}
int Query(int x) {
int res = 0;
for (int i = x; i; i -= i & (-i)) res += tree[i];
return res;
}
bool cmp(Node x, Node y) {
if (x.r == y.r) return x.l < y.l;
return x.r < y.r;
}
int main(){
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
m = read();
for (int i = 1; i <= m; i++)
A[i].l = read(), A[i].r = read(), A[i].id = i;
sort(A + 1, A + m + 1, cmp);
for (int i = 1; i <= m; i++) {
while (p <= A[i].r) {
if (last[a[p]]) Insert(last[a[p]], -1);
Insert(last[a[p]] = p, 1);
p++;
}
Ans[A[i].id] = Query(A[i].r) - Query(A[i].l - 1);
}
for (int i = 1; i <= m; i++) printf("%d\n", Ans[i]);
puts("");
return 0;
}
T4
给定长为 \(n\) 的序列, \(m\) 次查询区间有多少个值只出现一次。
solution
十分妙的二维平面转化。
对于一次查询 \([l, r]\) 中,一个数如果有贡献,那么左边第一个与它相同的数 < l 并且右边第一个与它相同的数 > r。
建立一个二维平面,横坐标为右端点,纵坐标为左端点,然后每个点就对应着一个询问。
一个点能产生贡献的范围在二维平面上就是一个矩形。
这样问题就转化为了二维平面上一个点被多少个矩形覆盖的问题了。
T5
给定一个长为 \(n\) 的序列,常数 \(w\) ,进行 \(m\) 次查询
每次查询给出的区间 \([l,r ]\) ,询问区间内是否存在两个数和为 \(w\) 。
solution
对于位置 \(i\) ,找到左边第一个满足 \(w - a[i] = a[j]\) 的 \(j\) , 此时 \(pre[i] = j\) ,对于查询区间内的所有数,如果都满足 \(pre[i]<l\) 那么这个区间就是不合法的。
现在就是求一段区间 \([l,r]\) 内的所有的数,是否所有的数都满足 \(pre[i] < l\) 。
怎么求?
求区间 \(pre[i]\) 的最大值,然后与 \(l\) 判断一下就好了。
\(RMQ\) 实现。
T6
给定一个长为 \(n\) 的序列,有 \(m\) 次询问。
每次查询区间内出现出现次数为奇数的异或和。
solution
。。。。
一时没反应过来 = =
区间出现偶数次的数异或都抵消了,这个题也就成了求区间的异或和。
T7
给定一个序列,每次查询区间中出现偶数次的数的异或和。(每种数只算一遍)
solution
区间出现奇数次的数的异或和已经会求了,那么出现次数为偶数的数的异或和就是出现过的数的异或和 ^ 出现次数为奇数的数的异或和。(出现次数为奇数的两次异或抵消了,剩下的只剩出现次数为偶数次的了。)
T8
给定二维平面上的 \(n\) 个矩形,矩形的坐标为 \([1, n]\) 内,求矩形的面积并。
solution
竖着维护一个扫描线,如果扫到一个矩形的左端点,就区间+1,如果是右端点就区间 -1,每走一步都要查询一下全局中大于 \(0\) 的个数加入答案,这样就退化到了暴力。可以维护区间等于 \(0\) 的个数,然后用全局减去 0 的个数就好了。
维护区间等于 \(0\) 的个数?
每个数都不可能小于 0,所以可以维护最小值,然后维护一个最小值的个数,判断最小值是否为 0,如果不是那就没有 0,否则最小值个数就是 0 的个数。
复杂度:\(O(n logn)\)
T9
维护一堆不等式
- 插入一个 \(ax + b > c\) 的不等式
- 删除第 \(i\) 个插入的
- 查询 \(x =k\) 的时候成立的不等式个数
solution
将不等式变形
当 \(a > 0\) 时,为 \(x > \frac{b - c}{a}\)
当 \(a < 0\) 时,为 \(x < \frac{b - c}{a}\)
当 \(a = 0\) 时,为 \(b > c\)
树状数组维护就好了。
T9
Codechef DGCD
给定一个长度为 \(n\) 的序列,有 \(m\) 次操作:
- 将区间加上 x
- 查询区间的最大公倍数
solution
\(gcd(a, b) = gcd(a - b, b)\)
\(gcd(a_l, a_{l + 1}, a_{l + 2} \dots a_{a_r}) = gcd(a_l, a_{l + 1} - a_l ,a_{l + 2} - a_{l + 1}\dots a_{r} - a_{r - 1})\)
另 \(b_i = a_i - a_{i - 1}\)
那么也就成了求 \(gcd(a_l, b_{l + 1}, b_{l + 2}\dots b_{r})\)
区间修改。
当在 \(a\) 数组对 \([l,r]\) 区间更改的时候,\(b\) 数组改变的只有 \(b_{l}\) 和 \(b_{r + 1}\) (手模一下)
然后区间修改就转化成立两个单点修改。
T10
维护一个长为 \(n\) 的字符串序列,有 \(m\) 次操作:
- 单点修改。
- 查询两个区间所对应的字符串是否完全一样。
solution
线段树维护区间的 \(hash\) 值,预处理出 base 数组,然后就可以区间合并了,单点修改直接修就好了。
T11
维护一个长为 \(n\) 的字符串序列。
- 单点修改。
- 查询两个区间所对应的字符串的 \(LCP\) 的长度
\(LCP:\) 两个字符串的最长公共前缀
solution
求 \(lcp\) 可以二分其长度,每次二分完判断 \(hash\) 值是否相等。
T12
给两个字符串 \(A,B\) ,长度分别为 \(n\) ,\(m\) 。
有 \(q\) 次操作
每次修改 \(B\) 的一个位置,或者查询 \(A\) 中有多少长为 \(m\) 的字串与 \(B\) 完全相同。
solution
\(O(n)\) 已处理出 \(A\) 串中长 \(m\) 的所有字串的 \(hash\) 值,用 \(map\) 存起来,然后用树状数组维护 \(B\) 的区间 \(hash\) 值,然后在 \(map\) 中插就好了。
T13
给出一棵树,\(n\) 个节点,\(m\) 次修改,每次给定 \(s\) 和 \(t\) ,把 \(s\) 到 \(t\) 路径上的点权都 +1,问 \(m\) 次操作后的最大点权
solution
对询问进行离线,然后树上差分就好了。
T14
给一个 \(n\) 个点的树,有点权,有 \(m\) 次查询,每次给出三个数 \(x, y, z\) ,求 \(x\) 到路径上有多少个点值 = z
solution
树上差分,先 \(dfs\) 一遍,每个点开个 \(map\) 把该点的权值在 \(map\) 中 +1,然后就可以求到一个点到根的路径上有多少个值等于 \(z\) ,求路径就差分一下就好了。
T15
给一个 \(n\) 个点的数,有点权,有 \(m\) 次查询,每次给三个数 \(x,y,z\) 求 \(x\) 到 \(y\) 之间有多少点值 < z;
solution
和上题一个样差分,因为求小于 \(z\) 的个数,用树状数组维护就好了。
T16
给一棵 \(n\) 个节点的树,有 \(m\) 次询问。
每次查询;
从 \(x\) 点开始向 \(y\) 走,每秒走一步
假设在第 \(i\) 秒走到了 \(i\) 点,则答案 +1.
solution
天天爱跑步
树上差分。
考虑 \(x\) 到 \(i\),如果 \(i\) 对答案有贡献,那么一定满足 \(dep_x - dep_i = i\) ,
从 \(y\) 到 \(i\) ,如果 \(i\) 有贡献一定满足 \(s - (dep_y - dep_i) = i\) \(s\) 为路径 \(x\) 到 \(y\) 长度。
这样差分一下就能做了。
T17 loj 6276
树,点有颜色,求多少条树上简单路径满足上面的颜色互不相同。
每种颜色出现次数 \(\leq 20\), \(n\leq 10^5\)
solution
如果有两个相同颜色的点,一条合法的路径两个端点一定不会分别在这两个点的子树内,选了构成的路径一定经过这两个颜色相同的点。
对于子树考虑 \(dfs\) 序处理。
两个相同颜色端点(不互为祖先)的子树都分别对应着 \(dfs\) 的一个区间,如果路径的左右端点在这个分别在这两个区间的话那么这条路径就不合法。
如果一个点与它一个祖先颜色相同,那么不合法的路径就是从该点的子树内选一个点,祖先的子树外选一个点,对应到 \(dfs\) 序上就是一段连续的区间和两端区间。
构造二维平面(一维为左端点,一维为右端点),每条路径都对应这二维平面内的一个点,每两个颜色相同的不互为祖先的点能形成的不合法的路径都是一个矩形,互为祖先的就形成两个矩形。
初始最大的矩形(所有的路径)值为 1,然后将构成的每个小矩形设为 0,最后统计有多少个 1 就好了
T18
给出一棵 \(n\) 个点的树,以及一个长为 \(n\) 的序列 \(a\),\(a_i\) 表示 \(a\) 序列 \(i\) 位置为一个树上编号为 \(a_i\) 的节点,树的边权为 \(1\)
有 \(m\) 次询问,每次询问给两个 \(a\) 的区间,求从两个区间中各选出一个点能得到的树上最远距离。
就是从 \([l_1,r_1]\) 中选一个 \(i\),\([l_2,r_2]\) 中选一个 \(j\),求 \(max~~ dist(a[i],a[j])\)
\(n\leq 10^5,m\leq 10^6\)
solution
树上的一个点 \(x\) 和一个点集,求找出点集中的一个点到 \(x\) 的路径长度最长:在点集中找出一个直径,然后所求就是两个端点中的较大一个。
根据这个性质可以线段树维护区间直径:新的直径的两个端点一定在左区间直径的两个端点和右区间直径的两个端点。
最后查询给出的两个区间的直径,与合并一样,枚举一下就好了;
距离可以用 ST 表实现。
T19
给出一个 \(n\) 个点的树,有点权
有 \(m\) 次查询,每次会把 \(x\) 的点权修改,或者查询 \(x\) 所在子树内的点权和。
solution
\(dfs\) 序上单点修改,查区间和。
T20
给一个 \(n\) 个点的树,有点权
有 \(m\) 次查询,每次会修改一个点的权值,或者查 \(x\) 到 \(y\) 路径上的点权和。
solution
树剖
树上差分。
单点修改查询点到根路径的点权和。
一个点修改,影响的只有其子树内的点,所以单点修改可以改为对 \(dfs\) 序上对它的子树进行区间修改。
T21
给定一棵 \(n\) 个点的树,以及一个初始为空的集合,集合里的数据类型为二元组
有 \(m\) 次操作
每次操作可能是在集合中插入一个二元组 \((x,y)\),删除一个二元组,或者查询树上一条边,是否满足,对所有集合中的二元组 \((a,b)\),\(a\) 到 \(b\) 的简单路径都经过了这条边,即假设删掉这条边,是否会将集合中所有二元组都分到了两个部分
solution
每插入一个二元组相当于路径 +1,删掉就是路径 -1,然后就是单点查询了。
T22
给定一个正数序列,输出前 \(k\) 小的子区间和。
solution
显然最小的一定是区间 \([1,1],[2,2],[3,3]\dots[n,n]\) 中最小的一个,把它们加入一个堆中。
因为区间的数都是正数,所以 \([l,r] < [l - 1, r],[l, r + 1]\) 。
依次取出 \(k\) 个数,取出一个 \([l, r]\) 就加入 $[l-1,r] $ 和 \([l, r + 1]\),因为加入的区间可能重复,\(map\) 去重就好了。