[CF765F]Souvenirs

codeforces

description

给你一个数列\(\{a_i\}\),每次询问一段区间\([l,r]\)内两数之差的最小值。
\(n\le10^5,m\le3\times10^5,a_i\le10^9\)

sol

首先询问离线,然后向右移动右端点,维护左端点的答案。为了方便,我们只考虑用\(j<i,a_j>a_i\)的点对去更新答案,剩下的可以把\(a_i\)的值域翻转然后再做一遍。
假设现在右端点在\(i\),我们先找到\(\max\{j|a_j>a_i\}\),这样就可以用\(a_j-a_i\)更新左端点在\([1,j]\)的答案。然后我们希望找一个\(j'<j\)满足\(a_{j'}\ge a_i\)\(a_{j'}-a_i<a_j-a_i\),这样就可以进一步更新答案。接着可以令\(j=j'\),重复之间的操作,直至找不到一个满足条件的\(j'\)为止。
但其实,这个限制条件可以进一步写为\(a_{j'}-a_i<a_j-a_{j'}\)
很简单,因为在这之前\(a_j-a_{j'}\)就已经更新过答案了,所以要想进一步更新除非答案更优。
移项可得\(a_{j'}-a_i<\frac 12(a_j-a_i)\)。发现每次\(a_j\)\(a_i\)的差至少减小一半,所以对于每一个\(i\),至多存在\(O(\log10^9)\)\(j\)可以更新答案。
那么复杂度就是\(O(n\log n\log10^9+m\log n)\)
至于怎么找到\(j'\),我直接写的主席树。相当于是找满足值域在一定范围内的下标最大值。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 3e5+5;
const int inf = 1e9;
struct node{int l,r,id;}q[N];
struct seg{int ls,rs,v;}t[N*40];
int n,m,a[N],rt[N],tot,c[N],ans[N];
vector<int>v[N];
void mdf(int k,int v){while(k)c[k]=min(c[k],v),k^=k&-k;}
int qry(int k){int s=ans[0];while(k<=n)s=min(s,c[k]),k+=k&-k;return s;}
void modify(int &x,int l,int r,int p,int v){
	t[++tot]=t[x];x=tot;t[x].v=max(t[x].v,v);
	if (l==r) return;int mid=l+r>>1;
	if (p<=mid) modify(t[x].ls,l,mid,p,v);
	else modify(t[x].rs,mid+1,r,p,v);
}
int query(int x,int l,int r,int ql,int qr){
	if (ql>qr) return 0;
	if (!x||(l>=ql&&r<=qr)) return t[x].v;
	int mid=l+r>>1,s=0;
	if (ql<=mid) s=max(s,query(t[x].ls,l,mid,ql,qr));
	if (qr>mid) s=max(s,query(t[x].rs,mid+1,r,ql,qr));
	return s;
}
void work(){
	memset(c,63,sizeof(c));memset(rt,0,sizeof(rt));
	for (int i=1;i<=tot;++i) t[i]=(seg){0,0,0};tot=0;
	for (int i=1;i<=n;++i) modify(rt[i]=rt[i-1],0,inf,a[i],i);
	for (int i=1;i<=n;++i){
		int j=query(rt[i-1],0,inf,a[i],inf);
		while (j){
			mdf(j,a[j]-a[i]);
			j=query(rt[j-1],0,inf,a[i],(a[i]+a[j]>>1)-(~(a[i]+a[j])&1));
		}
		for (int j:v[i]) ans[q[j].id]=min(ans[q[j].id],qry(q[j].l));
	}
}
int main(){
	n=gi();
	for (int i=1;i<=n;++i) a[i]=gi();
	m=gi();
	for (int i=1;i<=m;++i) q[i]=(node){gi(),gi(),i};
	memset(ans,63,sizeof(ans));
	for (int i=1;i<=m;++i) v[q[i].r].push_back(i);
	work();
	for (int i=1;i<=n;++i) a[i]=inf-a[i];
	work();
	for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2018-08-26 17:03  租酥雨  阅读(250)  评论(0编辑  收藏  举报