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. webservice:com.sun.xml.internal.ws.server.ServerRtException: [failed to localize]

    发布webservice发生了错误,一直没有能够解决,错误如下: Exception in thread "main" com.sun.xml.internal.ws.server ...

  2. 切换目录查询目录 tcp

    服务器 import socket import os import json sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() ...

  3. vscode开发c#

    转载自: http://www.cnblogs.com/lxhbky/p/6673230.html http://www.cnblogs.com/lxhbky/p/6692065.html 一.环境安 ...

  4. http之理解304

    原文:http://www.cnblogs.com/ziyunfei/archive/2012/11/17/2772729.html 如果客户端发送的是一个条件验证(Conditional Valid ...

  5. JPA环境配置

    JPA概述 JPA(Java Persistence API)的简称,用于持久化的API. JAVAEE5.0平台标准的ORM的规范使得应用程序以统一的方式访问持久层. JPA和Hibernate的关 ...

  6. Es6对象的扩展和Class类的基础知识笔记

    /*---------------------对象的扩展---------------------*/ //属性简写 ,属性名为变量名, 属性值为变量的值 export default functio ...

  7. java接口实现

    1.接口中的方法一定是public abstract方法所以类要继承实现接口的时候,一定要去掉abstract修饰符,而且还要标明方法的访问权限一定是public 声明接口不适用public就是友好的 ...

  8. tensorflow(3):神经网络优化(ema,regularization)

    1.指数滑动平均 (ema) 描述滑动平均: with tf.control_dependencies([train_step,ema_op]) 将计算滑动平均与 训练过程绑在一起运行 train_o ...

  9. uva11827 处理下输入

    /*0.012s*/ #include<cstdio> #include<algorithm> using namespace std; ], n; int gcd(int a ...

  10. 20165314 学习基础和C语言基础调查

    技能学习心得 你有什么技能比大多人(超过90%以上)更好?针对这个技能的获取你有什么成功的经验?与老师博客中的学习经验有什么共通之处? 从小我的父母就逼着我学习很多技能,比如钢琴,围棋,书法等,不过很 ...