Souvenirs

我们将询问离线, 我们从左往右加元素, 如果当前的位置为 i ,用一棵线段树保存区间[x, i]的答案,

每次更新完, 遍历R位于 i 的询问更新答案。

我们先考虑最暴力的做法, 我们先找到位于 i 前面第一个 j, a[ j ] > a[ i ], 那么x 属于 [ 1, j ]的答案

就会被a[ j ] - [ i ] 更新一下。 然后下一个找在 j 前面第一个 k, a[ k ] >= a[ i ] && a[ k ] < a[ j ], 这个过程

可以用一个主席树来维护。 但是这样的做法肯定会T掉, 实际上我们多了很多没有必要的更新。

我们找 k 的时候肯定要满足这个不等式, a[ k ] - a[ i ] < a[ j ] - a[ k ]    ->   a[ k ] < (a[ i ] + a[ j ] ) / 2, 这样

就只要重复log(max)次就能完成。

为什么这个不等式成立呢, 因为, a[ j ] - a[ k ] 这个值在 j 加进去的时候就更新了一次,没必要找比这个值大的。

#include<bits/stdc++.h>
#define LL long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ull unsigned long long using namespace std; const int N = 1e5 + ;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = ;
const double eps = 1e-;
const double PI = acos(-); int n, m, a[N], ans[N * ];
vector<PII> qus[N];
vector<int> oo; #define lson l, mid , rt << 1
#define rson mid + 1, r, rt << 1 | 1 namespace SGT1 {
int a[N << ];
void build(int l, int r, int rt) {
a[rt] = inf;
if(l == r) return;
int mid = l + r >> ;
build(lson); build(rson);
}
void update(int L, int R, int val, int l, int r, int rt) {
if(l >= L && r <= R) {
a[rt] = min(a[rt], val);
return;
}
int mid = l + r >> ;
if(L <= mid) update(L, R, val, lson);
if(R > mid) update(L, R, val, rson);
}
int query(int p, int l, int r, int rt) {
if(l == r) return a[rt];
int mid = l + r >> ;
if(p <= mid) return min(query(p, lson), a[rt]);
else return min(query(p, rson), a[rt]);
}
} namespace SGT2 {
int tot = , Rt[N];
struct Node {
int mx, ls, rs;
} a[N * ];
void init() {
tot = ;
}
void update(int p, int val, int l, int r, int& x, int y) {
x = ++tot; a[x] = a[y]; a[x].mx = max(a[x].mx, val);
if(l == r) return;
int mid = l + r >> ;
if(p <= oo[mid - ]) update(p, val, l, mid, a[x].ls, a[y].ls);
else update(p, val, mid + , r, a[x].rs, a[y].rs);
}
int query(int L, int R, int l, int r, int x) {
if(L > oo[r - ] || R < oo[l - ]) return ;
if(oo[l - ] >= L && oo[r - ] <= R) return a[x].mx;
int mid = l + r >> ;
if(R <= oo[mid - ]) return query(L, R, l, mid, a[x].ls);
else if(L > oo[mid - ]) return query(L, R, mid + , r, a[x].rs);
else return max(query(L, R, l, mid, a[x].ls), query(L, R, mid + , r, a[x].rs));
}
} void solve() {
oo.clear();
SGT1::build(, n, );
SGT2::init();
for(int i = ; i <= n; i++) oo.push_back(a[i]);
sort(oo.begin(), oo.end());
oo.erase(unique(oo.begin(), oo.end()), oo.end());
for(int i = ; i <= n; i++)
SGT2::update(a[i], i, , SZ(oo), SGT2::Rt[i], SGT2::Rt[i - ]);
for(int i = ; i <= n; i++) {
int cnt = ;
int now = SGT2::query(a[i], inf, , SZ(oo), SGT2::Rt[i - ]);
while(now) {
SGT1::update(, now, a[now] - a[i], , n, );
if(a[now] == a[i]) break;
int nxt = SGT2::query(a[i], a[i] + a[now] >> , , SZ(oo), SGT2::Rt[now - ]);
now = nxt;
}
for(auto& q : qus[i])
ans[q.se] = min(ans[q.se], SGT1::query(q.fi, , n, ));
}
} int main() {
memset(ans, inf, sizeof(ans));
scanf("%d", &n);
for(int i = ; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &m);
for(int i = ; i <= m; i++) {
int L, R; scanf("%d%d", &L, &R);
qus[R].push_back(mk(L, i));
}
solve();
for(int i = ; i <= n; i++) a[i] = -a[i];
solve();
for(int i = ; i <= m; i++) printf("%d\n", ans[i]);
return ;
} /*
*/

Codeforces 765F Souvenirs 线段树 + 主席树 (看题解)的更多相关文章

  1. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  2. Codeforces 765F Souvenirs - 莫队算法 - 链表 - 线段树

    题目传送门 神速的列车 光速的列车 声速的列车 题目大意 给定一个长度为$n$的序列,$m$次询问区间$[l, r]$内相差最小的两个数的差的绝对值. Solution 1 Mo's Algorith ...

  3. Codeforces.765F.Souvenirs(主席树)

    题目链接 看题解觉得非常眼熟,总感觉做过非常非常类似的题啊,就是想不起来=v=. 似乎是这道...也好像不是. \(Description\) 给定长为\(n\)的序列\(A_i\).\(m\)次询问 ...

  4. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  5. 小结:线段树 & 主席树 & 树状数组

    概要: 就是用来维护区间信息,然后各种秀智商游戏. 技巧及注意: 一定要注意标记的下放的顺序及影响!考虑是否有叠加或相互影响的可能! 和平衡树相同,在操作每一个节点时,必须保证祖先的tag已经完全下放 ...

  6. [学习笔记] 可持久化线段树&主席树

    众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...

  7. 权值线段树&&可持久化线段树&&主席树

    权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...

  8. 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))

    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...

  9. UOJ#218. 【UNR #1】火车管理 线段树 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...

随机推荐

  1. ES--05

    第四十一讲!分词器内部组成 内置分词器 课程大纲 1.什么是分词器 切分词语,normalization(提升recall召回率) 给你一段句子,然后将这段句子拆分成一个一个的单个的单词,同时对每个单 ...

  2. web@css高级选择器(after,befor用法),基本css样式

    1.高阶选择器:子代后代,相邻通用兄弟,交集并集,属性,伪类,伪元素子代后代选择器 div>p{}  div p{}相邻通用兄弟 div+p{}  div~p{}理解:div同学的同桌p  di ...

  3. sugarCRM文档翻译1

    2018-3-9 14:42:14 星期五 本文分两部分: 第一部分是从index.php入口开始的代码执行的部分流程 第二部分是对官方文档的翻译 第一部分: 流程: 入口文件: index.php ...

  4. javascript for循环 日期 select

    2016年12月28日 20:01:54 星期三 html: <a href="aaaa">AAAA</a> <a href="bbbb&q ...

  5. encoding and Endian

    Unicode, Code Point is the value of evry character in Unicode table(int,long,ll) Unicode defines a c ...

  6. SpringCloud的版本

    Spring Cloud 项目目前仍然是快速迭代期,版本变化很快.这里整理一下版本相关的东西,备忘一下. 大版本 版本号规则 Spring Cloud并没有熟悉的数字版本号,而是对应一个开发代号. C ...

  7. filter(HTML)滤镜用法

    CCS滤镜参考语法:STYLE="filter:filtername(fparameter1, fparameter2...)" (Filtername为滤镜的名称,fparame ...

  8. nginx+ssl 服务器 双向认证

    项目后台服务器采用nginx+tomcat 负载均衡架构  不久 访问协议有http升级为https 对服务器认证采用沃通的ssl证书 nginx ssl证书安装 参照沃通官方文档 他们有技术支持沟通 ...

  9. springboot第一个项目【创建】

    1.new project,不勾选create from archetype,直接选择 2.next下一步 在Maven依赖管理中,唯一标识一个依赖项是由该依赖项的三个属性构成的,分别是groupId ...

  10. Confluence 6 内存使用和需求

    管理 Confluence 性能和内存的使用和有什么资源可用是密切相关的.如果你给 Confluence 更多的内存来缓存内容,Confluence 将会运行得更加迅速,但是相对比较低的内存环境中,C ...