题目链接: P6617 查找 Search

大致题意

给定长度为nnn的整数序列, a1,a2,...,ana_1, a_2, ..., a_na1​,a2​,...,an​. 有m次操作:

pos val 表示把aposa_{pos}apos​改为valvalval.

l r 询问[l,r][l, r][l,r]区间, 是否存在ai+aj=w,(i≠j)a_i + a_j = w, (i \ne j)ai​+aj​=w,(i​=j).

解题思路

思维

我们考虑如果没有修改操作, 每次询问一个区间是否有两个不同位置的数加和等于www. 那么我们可以对于每个位置记录它前方第一个满足条件数字的位置index. 然后判断区间最大值indexiindex_iindexi​是否满足indexi≥lindex_i \ge lindexi​≥l即可.

下文将会用绑定代指两个符合要求的位置.

考虑到有了修改操作, 我们不能再采用上述的方式维护信息.

设w = x + y, 假设序列为 x, y, y, y, y, …, y.

这样当我们修改第一个位置时, 我们需要把后续所有的位置都修改, 很显然是可以卡成O(n2)O(n^2)O(n2)的复杂度的.


每个位置的元素只被其后方最近的元素记录.

设w=x+yw = x + yw=x+y. 考虑到对于序列a={x,y,y,y}a = \{ x, y, y, y \}a={x,y,y,y} 而言, 我们只需要把1, 2位置绑定即可.

相同元素所记录的位置区间不应有交集.

设w=x+yw = x + yw=x+y. 考虑到对于序列a={x,x,y,y}a = \{ x, x, y, y \}a={x,x,y,y} 而言, 我们只需要把2, 3位置绑定即可.


通过上述两个结论, 我们发现每个位置向前绑定, 与向后绑定的数字都可以唯一确定. 因此在修改某个位置时, 我们可以通过常数次操作去移除/添加当前位置产生的影响.


线段树

我们发现询问的本质是, 询问区间最大值, 因此我们可以采用线段树来动态维护最大值.

对于修改操作, 我们维护好每个位置的前驱后继(绑定)情况即可

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10;int w[N];
struct node { //线段树动态维护区间最大值int l, r;int fmax;
}t[N << 2];
void pushup(int x) { t[x].fmax = max(t[x << 1].fmax, t[x << 1 | 1].fmax); }void build(int l, int r, int x = 1) {t[x] = { l, r, 0 };if (l == r) return;int mid = l + r >> 1;build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}void modify(int a, int c, int x = 1) {if (t[x].l == t[x].r) {t[x].fmax = c;return;}int mid = t[x].l + t[x].r >> 1;modify(a, c, x << 1 | (a > mid));pushup(x);
}int ask(int l, int r, int x = 1) {if (l <= t[x].l and r >= t[x].r) return t[x].fmax;int mid = t[x].l + t[x].r >> 1;int res = 0;if (l <= mid) res = ask(l, r, x << 1);if (r > mid) res = max(res, ask(l, r, x << 1 | 1));return res;
}set<int> st[N]; //维护每个值出现的位置
int getnext(int x, int pos) { //找严格后继auto it = st[x].upper_bound(pos);if (it == st[x].end()) return 0;return *it;
}
int getprev(int x, int pos) { //找严格前驱auto it = st[x].lower_bound(pos);if (it == st[x].begin()) return 0;return *--it;
}
int link[N];
int main()
{int n, m, W; cin >> n >> m >> W;build(1, n);rep(i, n) {scanf("%d", &w[i]);int other = W - w[i];if (!st[other].empty()) { //防止==W/2int qaq = *st[other].rbegin(); //要配对的元素位置if (!link[qaq]) link[qaq] = i, modify(i, qaq);}st[w[i]].insert(i);}int cou = 0;rep(i, m) {int tp; scanf("%d", &tp);if (tp == 1) {int a, c; scanf("%d %d", &a, &c);if (c == w[a]) continue;int posprev = ask(a, a);if (posprev) { //前面link了东西int posnext = getnext(w[a], a);if (posnext and !ask(posnext, posnext)) { //后面有东西 且没有链接link[posprev] = posnext;modify(posnext, posprev);}else link[posprev] = 0;modify(a, 0);}if (link[a]) { //有被linkposprev = getprev(w[a], a); // 前一个位置if (posprev and !link[posprev]) { //该位置没有被linkmodify(link[a], posprev);link[posprev] = link[a];}else modify(link[a], 0);link[a] = 0;}st[w[a]].erase(a);w[a] = c;st[w[a]].insert(a);posprev = getprev(W - w[a], a);if (posprev) {if (link[posprev] > a) { // posprev  a  link[posprev]modify(link[posprev], 0);link[posprev] = a;modify(a, posprev);}else if (!link[posprev]) {link[posprev] = a;modify(a, posprev);}}int posnext = getnext(W - w[a], a);if (posnext) {posprev = ask(posnext, posnext);if (posprev < a) {  // posprev(0)   a   posnextlink[posprev] = 0;modify(posnext, a);link[a] = posnext;}}}else {int l, r; scanf("%d %d", &l, &r);l ^= cou, r ^= cou;int now = ask(l, r);if (now >= l) cou++, puts("Yes");else puts("No");}}return 0;
}

END

P6617 查找 Search (线段树)相关推荐

  1. P6617 查找 Search 线段树 查找区间内是否有两个和为w的数(w不变)

    题解: 每个点x,设置其前驱为离其最近的w-x的位置 每次修改可能影响O(n)个位置: w-x x x x x x x- 这样后面每个位置的前驱都是w-x 如果修改了w-x的值,这样会导致O(n)个修 ...

  2. poj 2352 Stars 线段树(先建后查/边建边查)/树状数组三种方法思路详解,带你深入了解线段树难度⭐⭐⭐★

    poj 2352 Stars 目录 poj 2352 Stars 1.树状数组 2.线段树,先建树后查找 3.线段树,边建树边查找 Description Astronomers often exam ...

  3. hdu1394 Minimum Inversion Number 线段树和树状数组

    题意: 输入一个长度 n 第二行给出长度为n的数组,数组的值刚好为0到n-1这n个数. 然后每次把数组的第一个数放到最后一个,放n-1次,共有n个排列,这n个排列就有n个逆序数,输出这n个逆序数的最小 ...

  4. HDU 1394 Minimum Inversion Number(线段树的单点更新)

    点我看题目 题意 :给你一个数列,a1,a2,a3,a4.......an,然后可以求出逆序数,再把a1放到an后,可以得到一个新的逆序数,再把a2放到a1后边,,,,,,,依次下去,输出最小的那个逆 ...

  5. hdu4973 线段树(题目不错,用了点,段,更新查找还有DFS)

    题意:       给你一个初始序列,初始序列长度n,分别为1 2 3 4 5 ....n,有两种操作 (1)D l r 把l_r之间的数据都复制一遍 1 2 3 4 5 6 D 2 4 = 1 2 ...

  6. hdu4267线段树段更新,点查找,55棵线段树.

    题意:      给你N个数,q组操作,操作有两种,查询和改变,查询就是查询当前的这个数上有多少,更改是给你a b k c,每次从a到b,每隔k的数更改一次,之间的数不更改,就相当于跳着更新. 思路: ...

  7. kb-07线段树-12--二分查找区间边界

    1 /* 2 hdu4614 3 本题刚开始想能不能记录该区间最前面开始的点,最后面的点,区间空的数量:但是病不行 4 然后线段树的本质是区间操作,所以!这题主要就是区间的空的全放满,只要定出区间的边 ...

  8. 线段树的创建插入查找删除

    一.线段树基本概念 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.     对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间 ...

  9. poj_2182 线段树/树状数组

    题目大意 n个数排成一排(不知道大小,只是占了一个位置),从a[1]到a[n]进行遍历,对于每个a[i],给出从a[1]到a[i-1]中小于a[i]数的个数.要求出 a[1]到a[n]中这n个数的相对 ...

最新文章

  1. linux怎么压缩后保留原文件,Linux 实现压缩保留源文件的方法
  2. redmine plugin
  3. jenkins配置git出现ERROR: Timeout after 10 minutes 同时命令行出现:Enter passphrase for key 的提示
  4. 621. Task Scheduler 任务调度器
  5. 【CSS 】动画animation
  6. python随机数据随概率分布_概率分布及其Python实现
  7. Java集合框架之Collection实例解析
  8. matlab低通滤波器库函数代码_Matlab中模拟低通滤波器的函数
  9. SqlSever2005 一千万条以上记录分页数据库优化经验总结【索引优化 + 代码优化】一周搞定...
  10. 室内设计——别墅设计方案(包含预览图jpg和.psd文件)
  11. 一篇经典的求职经历博客,值得深入研究和学习
  12. 消防报警图形显示装置linux,消防中控-消防控制室图形显示装置状态识别及操作...
  13. docker入门及安装及基本命令
  14. 【个人经验】关于BFM
  15. android http下载限速,安卓手机端两种让网盘不限速下载方法介绍
  16. Oracle 入门初学者安装(一)。
  17. 服务器阵列有什么作用,服务器存储-存储服务器和磁盘阵列有什么区别
  18. torch.arange
  19. html5左侧边导航右边显示页面,bootstrap如何实现左侧导航栏右侧网页
  20. java7u45下载_jdk-7u45-windowi586 32位 求官网

热门文章

  1. IDEA插件系列(19):EduTools插件——学习编程语言
  2. babel—ES6代码转换为ES5代码
  3. chtMultiRegionFoam求解器及算例分析
  4. 怎样才算是一个好的测试用例
  5. 单片机c语言基础知识,c语言必背100代码有哪些?
  6. ubuntu/linux系统管理(12)Ubuntu 22.04 安装文件比对工具Meld替代Beyond Compare
  7. PostgreSQL构建通用标签系统
  8. 《软件测试》读书笔记
  9. 【OpenCV】边缘检测:Sobel、拉普拉斯算子
  10. Numpy中reshape的用法