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 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...
随机推荐
- ansible笔记(2):清单配置详解
上一篇文章介绍了ansible的基本概念,以及相关的基础配置,我们已经知道,如果想要管理受管主机,则需要将受管主机添加到ansible的管理清单中,当安装ansible以后,会提供一个默认的管理清单, ...
- php7静态方法的链式调用
2018-1-11 20:25:48 星期四 情景: 以前想要链式调用必须先 new 一个对象, 然后 $obj->aa()->bb()... 现在PHP7 (php7.0.13 php ...
- mybatis:访问静态变量或方法
访问方法: <if test="@com.csget.constant.ConstantApp@getUser('mobile')== 'kf'"> <![CDA ...
- tornado的异步效果
第一种方式: import tornado.ioloop import tornado.web from tornado import gen from tornado.concurrent impo ...
- HashMap遍历的两种方式,推荐使用entrySet()
第一种: Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { ...
- 20)django-session使用
一:目录 1)session原理 2)cookie与session对比 3)session配置 4)session使用 5)示例 二:session原理 Django的Session机制会向请求的浏览 ...
- grep匹配某个次出现的次数
cat file | grep -c 'xxx' 统计xxx在file中出现的行数 cat file | grep -o 'xxx' 统计xxx在file中出现的次数
- Happiness
1575: Happiness 时间限制: 1 Sec 内存限制: 1280 MB 题目描述 Chicken brother is very happy today, because he atta ...
- 微信小程序 如何获取用户code
1.首先需要获取code 使用 wx.login({ success: function(res) { console.log(res);//这里的返回值里面便包含code }, fail: func ...
- ActiveMQ使用的设计模式
注:接收不需要连接池,而发送需要连接池,是因为,接收在启动项目时就要注册监听,数目是固定的,而发送则会随着时间数目不断在变动,需要连接池,性能更优. 重点代码: private static void ...