Codeforces 765F 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 线段树 + 主席树 (看题解)的更多相关文章
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- Codeforces 765F Souvenirs - 莫队算法 - 链表 - 线段树
题目传送门 神速的列车 光速的列车 声速的列车 题目大意 给定一个长度为$n$的序列,$m$次询问区间$[l, r]$内相差最小的两个数的差的绝对值. Solution 1 Mo's Algorith ...
- Codeforces.765F.Souvenirs(主席树)
题目链接 看题解觉得非常眼熟,总感觉做过非常非常类似的题啊,就是想不起来=v=. 似乎是这道...也好像不是. \(Description\) 给定长为\(n\)的序列\(A_i\).\(m\)次询问 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- 小结:线段树 & 主席树 & 树状数组
概要: 就是用来维护区间信息,然后各种秀智商游戏. 技巧及注意: 一定要注意标记的下放的顺序及影响!考虑是否有叠加或相互影响的可能! 和平衡树相同,在操作每一个节点时,必须保证祖先的tag已经完全下放 ...
- [学习笔记] 可持久化线段树&主席树
众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ...
- 权值线段树&&可持久化线段树&&主席树
权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
- UOJ#218. 【UNR #1】火车管理 线段树 主席树
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...
随机推荐
- WAV文件有多大?MP3文件有多大?使用Lame 压缩比是多少?
一.说明: 录音文件大小多少?用什么存比较合?我有500G的硬盘存录音能存多久?...... 这些东西常用常忘,索性一次性就分析清楚记下来,方便以后查阅,如果能帮到大家那就更好了. 二.计算方法: ...
- JS:判断是否是移动端
通过User-Agent判断 代码: if(navigator.userAgent.match(/mobile/i)) { //业务层代码 $('body').removeClass("si ...
- 硬盘性能测试工具fio
如何衡量云硬盘的性能 IOPS:每秒读/写次数,单位为次(计数).存储设备的底层驱动类型决定了不同的 IOPS. 吞吐量:每秒的读写数据量,单位为MB/s. 时延:IO操作的发送时间到接收确认所经过的 ...
- python字符串,列表常用操作
24天养成一个好习惯,第五天! 一.字符串需要掌握的操作 1.取值(索引取值)需要注意的是只能取,不能改 msg = 'hello world' print(msg[4]) 2.切片(顾头不顾尾) m ...
- 【进阶1-2期】JavaScript深入之执行上下文栈和变量对象(转)
这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/hZIpnkKqdQgQnK1BcrH6Nw 阅读笔记 JS是单线程的语言,执行顺序肯定是顺 ...
- Confluence 6 缓存性能优化
Confluence 的运行状态与缓存状态有这密切的关系.针对 Confluence 的管理员来说,尤其是大型站点的 Confluence 管理员,设置好缓存尤其显得关键. 希望修改缓存的大小: 进入 ...
- 配置一个 Confluence 6 环境
本部分对你 Confluence 的外部设置进行描述.包括有如何配置 Web 服务器,应用服务器,目录和文件等信息—— Confluence 运行所需要的所有环境.有关在服务器内部对配置进行修改的内容 ...
- Confluence 6 CSS 编辑技巧
开始编辑空间样式表 一个空间的样式表是你开始对 CSS 进行自定义编辑的好的开始.在空间样式表中,包含了你所有可以进行修改的元素.当你对空间样式表进行编辑的时候,空间样式表的修改只会对你修改的空间有效 ...
- clock gen sdk 代码笔记
int ClockConfig(void) { u32 DIVCLK_DIVIDE = 10; u32 CLKFBOUT_MULT = 53; u32 CLKFBOUT_FRAC = 625; u32 ...
- git教程笔记(二)
1.首先进入自己的项目文件 在GitHub上申请一个自己的账户信息.创建一个Repository.然后复制仓库的地址在gitbush中进行clone gitbush中进行远程clone 2.gitbu ...