Gushing over Programm|

BigSmall_En

园龄:3年2个月粉丝:3关注:5

2022-07-29 20:58阅读: 99评论: 0推荐: 0

CF765F 题解

CF765F Solution

大牛逼分块线段树题。

给出 \(n\) 以及一个长为 \(n\) 的序列 \(a\)。给出 \(m\) 组询问。

每组询问给出一个 \(l,r\),需要求出对于 \(i,j\in[l,r]\),且满足 \(i\neq j,|a_i-a_j|\) 的最小值。

\(1\leq n\leq 10^5,1\leq m\leq 3\times10^5,0\leq a_i\leq 10^9\)

题解

做法来自神仙

不强制在线,考虑将询问离线。

发现按照右端点排序,对于每个右端点 \(r\),维护一个 \(ans_i=\min|a_i-a_j|,j\in[i,r]\)

对于询问 \([l,r]\),答案就是 \(l\) 的后缀最小值。这个过程暴力做是 \(O(n^2)\) 的。

既然是后缀最小值,那么对于 \(ans_i\),它的值是否是 \(\min|a_i-a_j|\) 就没那么重要了,实际上,我们只需要保证 \(ans_{i\sim r}\) 中的答案是最优的即可。

考虑将右端点从 \(r-1\) 移动到 \(r\),已经维护好了右端点 \(r-1\) 时的 \(ans\) 满足 \([l,r]\) 的答案就是 \(ans\) 的一段后缀最小值。

证明:对于每个 \(ans_i\),只有 \(\log a_i\) 个位置让这个位置的答案必须被修改。

见图

image.png

假设目前的答案最优是 \(a_j-a_r\),中间的黑线为 \((a_i+a_j)/2\)

  1. \(a_r<mid\),那么最优答案更新为 \(a_r-a_i\)\(ans_i\) 的值减少到至少一半。
  2. \(a_r\geq mid\),那么最优答案仍然是 \(a_j-a_r\)\(ans_i\) 的值不用被修改,只需修改 \(ans_j\) 的值,同时 \(ans_j\) 的值也减半了。

对于其他的情况也是类似的。

至于如何实现修改时跳过的操作呢。

可以考虑用线段树处理区间信息,同时处理顺序从右往左(因为维护后缀最小值,如果左侧的区间的最优答案都没右侧小,那么左区间不用递归)。

至于如何得出区间最优答案,建树时用一个 vector 存下该区间的所有的数,并队 vector 排序,查找时只用二分即可。

建树的时间复杂度是 \(O(n\log^2 n)\),因为每个点最多被修改 \(\log_a\) 次,所以查询时总的时间复杂度 \(O(n\log n\log a)\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=300005,INF=0x3f3f3f3f;
int n,m,a[N];
struct querys{int l,r,id;}que[N];
inline bool cmp(querys x,querys y){
	return x.r==y.r?x.l<y.l:x.r<y.r;
}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
vector<int>lis[N<<2];
int minv[N<<2],lim,ans[N];
inline void pushup(int p){minv[p]=min(minv[ls],minv[rs]);}
void build(int p,int l,int r){
	minv[p]=INF;
	for(int i=l;i<=r;++i)lis[p].push_back(a[i]);
	sort(lis[p].begin(),lis[p].end());
	if(l==r)return;
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(p);
}
void update(int p,int l,int r,int R,int x){
	if(1<=l&&r<=R){
		auto it=lower_bound(lis[p].begin(),lis[p].end(),x);
		if(it!=lis[p].end())minv[p]=min(minv[p],*it-x);
		if(it!=lis[p].begin())minv[p]=min(minv[p],x-*(it-1));
		if(minv[p]>=lim)return;
	}
	if(l==r){lim=min(lim,minv[p]);return;}
	if(R>mid)update(rs,mid+1,r,R,x);//先改右区间再改左区间可以剪枝
	update(ls,l,mid,R,x);
	pushup(p);lim=min(lim,minv[p]);
}
int query(int p,int l,int r,int L){
	if(L<=l&&r<=n)return minv[p];
	int tmp=INF;
	if(L<=mid)tmp=min(tmp,query(ls,l,mid,L));
	tmp=min(tmp,query(rs,mid+1,r,L));
	return tmp;
}

int main(){
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	m=read();
	for(int i=1;i<=m;++i)que[i].l=read(),que[i].r=read(),que[i].id=i;
	sort(que+1,que+1+m,cmp);
	build(1,1,n);
	int loc=1;
	while(que[loc].r<=1)++loc;
	for(int i=2;i<=n;++i){
		lim=INF;
		update(1,1,n,i-1,a[i]);
		while(que[loc].r==i){
			ans[que[loc].id]=query(1,1,n,que[loc].l);
			++loc;
		}
	}
	for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
	return 0;
}

本文作者:BigSmall_En

本文链接:https://www.cnblogs.com/BigSmall-En/p/16533543.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BigSmall_En  阅读(99)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起