CF765F Souvenirs

  • problem
  • solution
  • code

problem

题目链接

solution

这个势能线段树简直是太巧妙了!!!( ఠൠఠ )ノ

将询问按右端点升序离线下来。

对于每一个右端点 rrr,维护 ansi=min⁡{∣ai−aj∣,j∈[i,r]}ans_i=\min\{|a_i-a_j|,j\in[i,r]\}ansi​=min{∣ai​−aj​∣,j∈[i,r]}。

用线段树查询区间 [l,r][l,r][l,r] 内的 min⁡{ansi,i∈[l,r]}\min\{ans_i,i\in[l,r]\}min{ansi​,i∈[l,r]} 就是答案了。

时间复杂度的瓶颈在于修改维护线段树上面。暴力做是 O(n2)O(n^2)O(n2) 的。

考虑优化。假设 ansians_iansi​ 的最优选择点在 jjj。

  • 如果 ara_rar​ 是在黄色点,即 <ai+aj2<\frac{a_i+a_j}{2}<2ai​+aj​​ ,这个时候要必须更新 ansians_iansi​。这会让 ansians_iansi​ 的值变小至少一半。

  • 如果 ara_rar​ 是在蓝色点,即 >ai+aj2>\frac{a_i+a_j}{2}>2ai​+aj​​,这个时候直接更新 ansjans_jansj​ 而不再更新 ansians_iansi​。

    因为 j>i∧ansj<ansij>i\wedge ans_j<ans_ij>i∧ansj​<ansi​ ,最后 rrr 的询问肯定答案是不会选到 ansians_iansi​ 的。

    所以就算 ansians_iansi​ 是错的,也不影响!

  • 如果 ara_rar​ 在 aia_iai​ 等值线下面,也是一样的。

将 ansians_iansi​ 看作势能,递减到 000 时就不再更新。每次更新至少减少一半。

所以修改点数应该是 nlog⁡an\log anloga 的。

具体而言:先修改右区间,再修改左区间。且如果当前修改的答案不如之前更新的答案,就直接跳过即可。同时需要存储下区间内所有的 aaa。

这样实际操作的只有必须更新的点。

总时间复杂度为:O(nlog⁡nlog⁡a)O(n\log n\log a)O(nlognloga)

code

#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 400005
#define inf 0x7f7f7f7f
vector < int > G[maxn];
struct node { int l, r, id; }q[maxn];
int n, Q;
int a[maxn], ans[maxn];namespace SegMentTree {int ans = inf;int Min[maxn];#define lson now << 1#define rson now << 1 | 1void build( int now, int l, int r ) {Min[now] = inf;for( int i = l;i <= r;i ++ ) G[now].push_back( a[i] );sort( G[now].begin(), G[now].end() );if( l == r ) return;int mid = ( l + r ) >> 1;build( lson, l, mid );build( rson, mid + 1, r );}/*void modify( int now, int l, int r, int R, int x ) {if( R < l ) return;if( r <= R ) {auto it = lower_bound( G[now].begin(), G[now].end(), x );if( it != G[now].end() ) Min[now] = min( Min[now], (*it) - x );if( it != G[now].begin() ) Min[now] = min( Min[now], x - *(it - 1) );if( Min[now] >= ans ) return;}if( l == r ) { ans = min( ans, Min[now] ); return; }int mid = ( l + r ) >> 1;modify( rson, mid + 1, r, R, x ); //优先更新最近的点modify( lson, l, mid, R, x );Min[now] = min( Min[lson], Min[rson] );ans = min( ans, Min[now] );}*/void modify( int now, int l, int r, int R, int x ) {if( R < l ) return;if( r <= R ) {it = lower_bound( G[now].begin(), G[now].end(), x );int tmp = inf;if( it != G[now].end() ) tmp = *it - x;if( it != G[now].begin() ) it--, tmp = min( tmp, x - *it );Min[now] = min( Min[now], tmp );if( tmp >= ans ) return;}if( l == r ) { ans = min( ans, Min[now] ); return; }modify( rson, mid + 1, r, R, x );ans = min( ans, Min[rson] );modify( lson, l, mid, R, x );Min[now] = min( Min[lson], Min[rson] );ans = min( ans, Min[now] );}int query( int now, int l, int r, int L ) {if( r < L ) return inf;if( L <= l ) return Min[now];int mid = ( l + r ) >> 1;return min( query( lson, l, mid, L ), query( rson, mid + 1, r, L ) );}}int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );scanf( "%d", &Q );for( int i = 1;i <= Q;i ++ ) scanf( "%d %d", &q[i].l, &q[i].r ), q[i].id = i;SegMentTree :: build( 1, 1, n );sort( q + 1, q + Q + 1, []( node x, node y ) { return x.r < y.r; } );int ip = 1;while( q[ip].r <= 1 ) ip ++;for( int i = 2;i <= n;i ++ ) {SegMentTree :: ans = inf;SegMentTree :: modify( 1, 1, n, i - 1, a[i] );while( q[ip].r == i )ans[q[ip].id] = SegMentTree :: query( 1, 1, n, q[ip].l ), ip ++;}for( int i = 1;i <= Q;i ++ ) printf( "%d\n", ans[i] );return 0;
}

Upd:感谢评论区的 hack\text{hack}hack 数据让我发现自己的代码写得有点问题。
和小同志研究了一天发现是代码注释部分的问题。
大概就是,当前 iii 插入时,按照分析应该是遇到更优秀 jjj 就不再往左子树找了。
这个更优秀是和以前存的答案比较。
但是之前代码实现是和现在被覆盖的答案比较。势能就差得离谱。
现在新代码在叶子节点时输出访问次数就大概是 111。

但是。。。呃呃呃呃,现在这份代码加上读优跑评论区的数据最快也要 5s5s5s。。。
我们讨论了一下我这份代码的写法,更偏向 nlog⁡n2log⁡Vn\log n^2\log Vnlogn2logV 的时间复杂度,因为要走 log⁡n\log nlogn 层线段树,每层内还有个 vector\text{vector}vector 的二分。
可能把二分写在外面能少个 log\text{log}log 的嵌套。
但是跑评论区的数据每个点都只被访问了一次,时间复杂度我就不是很懂了?。

CF765F Souvenirs(势能线段树)相关推荐

  1. 势能线段树/吉司机线段树-我没有脑子

    势能线段树/吉司机线段树 BZOJ3211 花神游历各国 BZOJ5312 冒险 BZOJ4355 Play with sequence BZOJ4695 最假女选手 \(A_i = max(A_i, ...

  2. 势能线段树(均摊分析)

    势能线段树 线段树能够通过打懒标记实现区间修改的条件有两个: 能够快速处理懒标记对区间询问结果的影响 能够快速实现懒标记的合并 但有的区间修改不满足上面两个条件(如区间整除/开方/取模等). 但某些修 ...

  3. 势能线段树(吉司机线段树)专题

    势能线段树(吉司机线段树)专题 势能线段树在近期训练时遇到了好几次,但是由于本人太懒一直没补完,结果ICPC网络赛还真就出了一道势能线段树Orz--结果当然是没做出来--痛定思痛,这回把之前欠的一块儿 ...

  4. 【题型总结】势能线段树

    吉老师势能线段树论文 ABC256Ex - I like Query Problem 题目链接 1 L R x:对 i = L , L + 1 , ⋯ , R i=L,L+1,\cdots,R i=L ...

  5. [BZOJ5312]冒险(势能线段树)

    [BZOJ5312]冒险 维护一个长度为 n 的序列,支持 m 次操作,操作包括区间按位或一个数,区间按位与一个数,以及查询区间最大值. 线段树每个节点上维护区间与.区间或和区间最大值. 如果一次操作 ...

  6. 2021CCPC东北四省赛 D. Lowbit 势能线段树

    传送门 分析 分析一下x+lowbit(x)x + lowbit(x)x+lowbit(x)这个操作 如果多次操作之后,那么xxx中只会有最高位存在一,这个时候再执行一次操作就会使整个数字乘二 所以, ...

  7. 「雅礼集训」市场--势能线段树

    题目链接: https://loj.ac/p/6029 题意: 思路: 势能时间分析: 每次除数至少为2,所以一个数最多除log次,log次操作后一个区间就会相等,加法又需要log次使区间相等,所以使 ...

  8. 数据结构二之线段树Ⅱ——KiKi‘s K-Number,ball,The Child and Sequence,「雅礼集训 2017 Day1」市场,Atlantis

    值域线段树+势能线段树+扫描线 KiKi's K-Number ball The Child and Sequence 「雅礼集训 2017 Day1」市场 Atlantis KiKi's K-Num ...

  9. CF 1642 E. Anonymity Is Important 线段树 + 离线

    文章目录 题意 思路 传送门 题意 有nnn个人,给你qqq个请求,分以下三种: [l,r,x][l,r,x][l,r,x] 如果x=0x=0x=0,代表[l,r][l,r][l,r]这个区间内的人都 ...

最新文章

  1. 传感器为什么在低量程偏差大_传感器的静态特性
  2. 详解Nginx SSL快速双向认证配置(脚本)
  3. 通过SWD J-Link使用J-Link RTT Viewer来查看打印日志
  4. linux tcp keepalive,[20170504]Linux TCP keepalive timers.txt
  5. boost::proto::noinvoke相关的测试程序
  6. MySQL中int、char、varchar的性能浅谈
  7. Kernel Newbies内核开发新手的资源
  8. 解决 Unable to translate SQLException with Error code ‘17059‘, will now try the fallback translator
  9. 服务发现与负载均衡traefik ingress
  10. java 执行多个查询语句_用java在单个语句中执行的多个查询
  11. [CodeForces332E]Binary Key
  12. Ext JS的模块化开发(Package)
  13. DB2 exception: Cannot create PoolableConnectionFactory SQLCODE=-142
  14. 【自然语言处理系列】预训练模型原理和实践综述 | 附汇报PPT原稿和18篇论文
  15. MySQL 函数:IF(expr,v1,v2) 判断
  16. micropython 播放音乐_用 pyboard 的 DAC 播放 WAV 格式音乐
  17. 第三集 be 动词一般动词的过去式
  18. 【DFS】巧妙取量的倒油问题
  19. web(html运行)设置字体大小、样式及字体名称
  20. 关于《一种鱼眼图象到透视投影图象的变换模型》

热门文章

  1. 趣图:你能Get到笑点么?
  2. java中有stdin_在java中听stdin的后台进程
  3. 因子分析最少要有几个因子_Re0:魔女司教和魔女是什么关系?他们的魔女因子是魔女给的吗...
  4. php 建立自己的框架,利用 Composer 一步一步构建自己的 PHP 框架(一)——基础准备...
  5. oracle绑定变量过多,oracle - 在SQL Plus中使用绑定变量并返回多行? - 堆栈内存溢出...
  6. 创维linux进入工厂模式,创维电视怎么进入工厂模式?
  7. android 监听布局改变,Android通过监听最外层布局的改变监听键盘的状态,软键盘的弹出和收起都会改变外层布局(前提是把Activity的mode设置成压缩);...
  8. php acl rbac,建站常用的用户权限管理模型ACL和RBAC的区别
  9. python如何使用geotools_基于GeoTools实现道路结点的提取
  10. ncl 添加点shp文件_气象编程 | NCL高效快速精准提取不规则区域内的格点数据