BZOJ2221:[Jsoi2009]面试的考验 单调性+随机数据

思路:

宋文杰的题解很详细.

一开始每个点30s,结果在OJ上一共30s...

也就是说原来莫队是能过的,结果我无悬念TLE.

莫队怎么做我就不说了.(貌似官方正解就是莫队...)

莫队很暴力,因为没有利用一个题目中的性质:就是数据随机生成!

询问随不随机意义是不大的.

但是如果询问很特殊,我们就可以用科学的方法去处理:

(1)任意两个区间不互相包含.这种情况我们将区间随便排个序就能发现总移动次数是\(O(n)\)的.

(2)任意两个区间要么互相包含,要么不相交.这样就会形成一棵树.我们利用启发式合并,插入次数就为\(O(nlogn)\).

但是随机数据显然没有这种性质.

 

那么唯一可能有比较优秀的性质的就是随机数列了.

考虑一种朴素算法:

将所有询问离线按照右端点从小到大排序.

从前向后依次加入右端点,令\(f_i\)表示当前以\(i\)为起点的后缀的答案,那么朴素来看,如果当前加入的是\(n\),那么\(f_1-f_{n-1}\)都需要更新.

但是有很多冗余的更新.

例如我们考虑所有大于\(a_n\)的数,不妨令\(a_i,a_j>a_n,a_i>a_j,i<j\),那么如果我们能够维护一个后缀的答案,我们就只需更新\(f_j\)就可以了.

于是我们发现了某些单调性.

我们利用数据结构维护后缀的答案,那么只需更新一些点就行了.

那么我们的任务就是找到,对于\(n\)之前的数,一个均\(>a_n\)的递减序列,一个均\(<a_n\)的递增序列.然后我们就拿序列中的东西暴力更新答案.

然而由于是随机数据,序列中的元素个数是\(O(logn)\)级别的.

这样总时间复杂度就是\(O(qlogn+nlog^2n)\).

具体去看宋文杰的题解.

(另外此题要求结果\(>0\) 23333)

 

#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 100010
#define Q 100010
int n,q,a[N];

struct Interval{
	int l,r,lab;
	bool operator<(const Interval&B)const{return r<B.r;}
}S[N];
int re[Q];

inline void mini(int&x,int y){if(x>y)x=y;}
struct SegmentTree{
	int A[262144],M;
	inline void Init(int _siz){
		for(M=1;M<(_siz+2);M<<=1);
		memset(A,0x3f,sizeof A);
	}
	inline void modify(int x,int d){
		for(x+=M;x;x>>=1)mini(A[x],d);
	}
	inline int query(int tl,int tr){
		int re=~0U>>1;
		for(tl+=M-1,tr+=M+1;tl^tr^1;tl>>=1,tr>>=1){
			if(~tl&1)mini(re,A[tl^1]);
			if(tr&1)mini(re,A[tr^1]);
		}
		return re;
	}
}Seg;

int preless[N],premore[N],small[N][30],big[N][30],stk[N],top;

int main(){
	//freopen("tt.in","r",stdin);
	scanf("%d%d",&n,&q);
	register int i,j,k;
	
	for(i=1;i<=n;++i)scanf("%d",&a[i]);
	
	for(i=1;i<=q;++i)
		scanf("%d%d",&S[i].l,&S[i].r),S[i].lab=i;
	sort(S+1,S+q+1);
	
	Seg.Init(n);
	
	for(i=1;i<=n;++i){
		while(top&&a[stk[top]]>=a[i])--top;
		preless[i]=stk[top];
		stk[++top]=i;
	}
	top=0;
	for(i=1;i<=n;++i){
		while(top&&a[stk[top]]<=a[i])--top;
		premore[i]=stk[top];
		stk[++top]=i;
	}
	
	for(i=1;i<=n;++i){
		if(premore[i]){
			big[i][++big[i][0]]=premore[i];
			j=big[i][1];
			while(1){
				bool find=0;
				for(k=1;k<=small[j][0];++k)if(a[small[j][k]]>a[i]){find=1;break;}
				if(find)big[i][++big[i][0]]=(j=small[j][k]);else break;
			}
		}
		if(preless[i]){
			small[i][++small[i][0]]=preless[i];
			j=small[i][1];
			while(1){
				bool find=0;
				for(k=1;k<=big[j][0];++k)if(a[big[j][k]]<a[i]){find=1;break;}
				if(find)small[i][++small[i][0]]=(j=big[j][k]);else break;
			}
		}
	}
	j=1;
	for(i=1;i<=n;++i){
		for(k=1;k<=small[i][0];++k)Seg.modify(small[i][k],a[i]-a[small[i][k]]);
		for(k=1;k<=big[i][0];++k)Seg.modify(big[i][k],a[big[i][k]]-a[i]);
		
		for(;j<=q&&S[j].r==i;++j)re[S[j].lab]=Seg.query(S[j].l,S[j].r);
		
	}
	
	for(i=1;i<=q;++i)printf("%d\n",re[i]);
	
	return 0;
}