qbxt 数据结构 (lxl)

又是lxl..(太好了,重复的题直接跳过了= =)

T1

给定一个序列,查询区间小于等于 k 的元素的个数。

solution

每个位置开个 vector 把询问两个端点离线下来。

然后维护一个值域树状数组,每个询问差分一下就好了。

T2

给定一个二维平面,上面有 n 个矩形,每个矩形的坐标为 [1,n]

m​ 次查询,每次查询一个二维平面上的点被多少矩形覆盖。

solution

扫描线。

把询问离线下来,按照 x 排序。

然后以 y​​ 轴为下标建一棵线段树,然后一条扫描线从左到右扫,如果扫到一个矩形的左端点就区间加,如果扫到一个矩形的右端点就区间减,查询就是单点查询了。

T3

P1972 [SDOI2009]HH的项链

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 ,找到左边第一个满足 wa[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(nlogn)

T9

P5482 [JLOI2011]不等式组

维护一堆不等式

  • 插入一个 ax+b>c 的不等式
  • 删除第 i 个插入的
  • 查询 x=k 的时候成立的不等式个数

solution

将不等式变形

a>0​ 时,为 x>bca

a<0 时,为 x<bca

a=0 时,为 b>c

树状数组维护就好了。

T9

Codechef DGCD

给定一个长度为 n 的序列,有 m 次操作:

  • 将区间加上 x
  • 查询区间的最大公倍数

solution

gcd(a,b)=gcd(ab,b)

gcd(al,al+1,al+2aar)=gcd(al,al+1al,al+2al+1arar1)

bi=aiai1

那么也就成了求 gcd(al,bl+1,bl+2br)​​

区间修改。

当在 a 数组对 [l,r] 区间更改的时候,b​ 数组改变的只有 blbr+1 (手模一下)

然后区间修改就转化成立两个单点修改。

T10

维护一个长为 n 的字符串序列,有 m 次操作:

  • 单点修改。
  • 查询两个区间所对应的字符串是否完全一样。

solution

线段树维护区间的 hash 值,预处理出 base 数组,然后就可以区间合并了,单点修改直接修就好了。

T11

[JSOI2008]火星人

维护一个长为 n 的字符串序列。

  • 单点修改。
  • 查询两个区间所对应的字符串的 LCP 的长度

LCP: 两个字符串的最长公共前缀

solution

lcp 可以二分其长度,每次二分完判断 hash 值是否相等。

T12

遥远的过去

给两个字符串 A,B ,长度分别为 nm

q 次操作

每次修改 B 的一个位置,或者查询 A 中有多少长为 m 的字串与 B 完全相同。

solution

O(n) 已处理出 A 串中长 m 的所有字串的 hash 值,用 map 存起来,然后用树状数组维护 B 的区间 hash 值,然后在 map 中插就好了。

T13

给出一棵树,n 个节点,m 次修改,每次给定 st ,把 st 路径上的点权都 +1,问 m 次操作后的最大点权

solution

对询问进行离线,然后树上差分就好了。

T14

给一个 n 个点的树,有点权,有 m 次查询,每次给出三个数 x,y,z ,求 x 到路径上有多少个点值 = z

solution

树上差分,先 dfs 一遍,每个点开个 map 把该点的权值在 map​ 中 +1,然后就可以求到一个点到根的路径上有多少个值等于 z ,求路径就差分一下就好了。

T15

给一个 n 个点的数,有点权,有 m 次查询,每次给三个数 x,y,zxy 之间有多少点值 < z;

solution

和上题一个样差分,因为求小于 z 的个数,用树状数组维护就好了。

T16

给一棵 n 个节点的树,有 m 次询问。

每次查询;

x 点开始向 y 走,每秒走一步

假设在第 i 秒走到了 i 点,则答案 +1.

solution

天天爱跑步

树上差分。

考虑 xi,如果 i 对答案有贡献,那么一定满足 depxdepi=i

yi ,如果 i 有贡献一定满足 s(depydepi)=i s 为路径 xy 长度。

这样差分一下就能做了。

T17 loj 6276

树,点有颜色,求多少条树上简单路径满足上面的颜色互不相同。

每种颜色出现次数 20n105

solution

如果有两个相同颜色的点,一条合法的路径两个端点一定不会分别在这两个点的子树内,选了构成的路径一定经过这两个颜色相同的点。

对于子树考虑 dfs​ 序处理。

两个相同颜色端点(不互为祖先)的子树都分别对应着 dfs​​​​ 的一个区间,如果路径的左右端点在这个分别在这两个区间的话那么这条路径就不合法。

如果一个点与它一个祖先颜色相同,那么不合法的路径就是从该点的子树内选一个点,祖先的子树外选一个点,对应到 dfs 序上就是一段连续的区间和两端区间。

构造二维平面(一维为左端点,一维为右端点),每条路径都对应这二维平面内的一个点,每两个颜色相同的不互为祖先的点能形成的不合法的路径都是一个矩形,互为祖先的就形成两个矩形。

初始最大的矩形(所有的路径)值为 1,然后将构成的每个小矩形设为 0,最后统计有多少个 1 就好了

T18

给出一棵 n 个点的树,以及一个长为 n 的序列 aai 表示 a 序列 i 位置为一个树上编号为 ai 的节点,树的边权为 1

m 次询问,每次询问给两个 a 的区间,求从两个区间中各选出一个点能得到的树上最远距离。

就是从 [l1,r1] 中选一个 i[l2,r2] 中选一个 j,求 max  dist(a[i],a[j])

n105,m106

solution

树上的一个点 x 和一个点集,求找出点集中的一个点到 x 的路径长度最长:在点集中找出一个直径,然后所求就是两个端点中的较大一个。

根据这个性质可以线段树维护区间直径:新的直径的两个端点一定在左区间直径的两个端点和右区间直径的两个端点。

最后查询给出的两个区间的直径,与合并一样,枚举一下就好了;

距离可以用 ST 表实现。

T19

给出一个 n 个点的树,有点权

m 次查询,每次会把 x 的点权修改,或者查询 x 所在子树内的点权和。

solution

dfs 序上单点修改,查区间和。

T20

给一个 n 个点的树,有点权

m 次查询,每次会修改一个点的权值,或者查 xy 路径上的点权和。

solution

树剖

树上差分。

单点修改查询点到根路径的点权和。

一个点修改,影响的只有其子树内的点,所以单点修改可以改为对 dfs​ 序上对它的子树进行区间修改。

T21

给定一棵 n 个点的树,以及一个初始为空的集合,集合里的数据类型为二元组

m 次操作

每次操作可能是在集合中插入一个二元组 (x,y),删除一个二元组,或者查询树上一条边,是否满足,对所有集合中的二元组 (a,b)ab 的简单路径都经过了这条边,即假设删掉这条边,是否会将集合中所有二元组都分到了两个部分

solution

每插入一个二元组相当于路径 +1,删掉就是路径 -1,然后就是单点查询了。

T22

给定一个正数序列,输出前 k 小的子区间和。

solution

显然最小的一定是区间 [1,1],[2,2],[3,3][n,n] 中最小的一个,把它们加入一个堆中。

因为区间的数都是正数,所以 [l,r]<[l1,r],[l,r+1]

依次取出 k 个数,取出一个 [l,r] 就加入 [l1,r][l,r+1],因为加入的区间可能重复,map 去重就好了。

posted @   Dita  阅读(820)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
主题色彩