O(n)预处理ST表RMQ学习

膜法!O(n)STABLE!!!

众所周知,朴素的ST表的时间复杂度是预处理O(n)+询问O(1)的。而这里有一个神奇的O(n)stable表(题目用的是LuoguP2880)

首先我们分块,块的大小是(log2(n)) (这个块长是有用的),然后一个一个分块。然后我们处理以下的几个数组。

minqianzhui[n]   maxqianzhui[n],minhouzhui[n] , maxhouzhui[n] 分别是前缀最小值和前缀最大值,后缀最小值和后缀最大值。这里的前缀后缀是针对分块后的数组来说的。也就是对于一个块内的前后缀。

以及fkminn[][] ,fkmaxx[], 这里是对块与块之间建立朴素ST表RMQ,fkminn[x][0]即对于x块的最小值,fkmaxx[x][2],即对于[x,x+(1<<2)-1]这几个块里面的最大值。(这里的建立时间复杂度小于O(n))

之后我们就可以了乱搞了!考虑到[l,r]区间,如果l到r之间不是同一个块内,那么我们可以O(1)找出中间块的最大最小值,以及通过后缀l和前缀r找出来。

只有当不是一个块的时候,我们就暴力乱扫吧!由于一个块的长度为logn,所以实际上最差也不会差到qlogn。

数据可能卡你始终一个块?绝不可能。题目数据范围肯定不可能一直出在一个小范围-----不然岂不是暴力都可以过?

所以可以达到期望时间复杂度预处理O(n)+O (q)啦!

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int n,q;
int a[maxn];
int blk,ks;
int fk[maxn];
int minqianzhui[maxn],minhouzhui[maxn],maxqianzhui[maxn],maxhouzhui[maxn];//前缀最大\小值,后缀最大\小值 
int logg[maxn];
int fkminn[maxn][20],fkmaxx[maxn][20];//分块rmq
int main()
{
	scanf("%d%d",&n,&q);
	logg[0]=-1;
	for(int i=1;i<=n;i++) logg[i] = logg[i>>1]+1;//预处理log2 
	blk = logg[n];
	int j=1;
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		fk[i] = j;
		if(fk[i]==fk[i-1]) 
		{ 
			minqianzhui[i]=min(minqianzhui[i-1],a[i]);
			maxqianzhui[i]=max(maxqianzhui[i-1],a[i]);
		} 
		else
		{
			minqianzhui[i]=maxqianzhui[i]=a[i];
		}
		if(!(i%blk)) j++;
	}
	if(n%blk) ks=j;
	else ks=j-1;
	for(int i=n;i;i--)
	{
		if(fk[i]==fk[i+1])
		{
			minhouzhui[i]=min(minhouzhui[i+1],a[i]);
			maxhouzhui[i]=max(maxhouzhui[i+1],a[i]);
		}
		else
		{
			minhouzhui[i]=maxhouzhui[i]=a[i];
		}
	}
	for(int i=1;i<=ks;i++)
	{
		fkminn[i][0] = minhouzhui[blk*(i-1)+1];
		fkmaxx[i][0] = maxhouzhui[blk*(i-1)+1];
	}
	for(int j=1;j<=logg[ks];j++)
	{
		for(int i=1;i+(1<<j)-1<=ks;i++)
		{
			fkminn[i][j] = min( fkminn[i][j-1] , fkminn[i+(1<<(j-1))][j-1]  ); //对分块间rmq 
			fkmaxx[i][j] = max( fkmaxx[i][j-1] , fkmaxx[i+(1<<(j-1))][j-1] );
		}
	}
	for(int i=1;i<=q;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		int fa = fk[l], fb =fk[r];
		if(fa!=fb)
		{
			fa+=1; fb-=1;
			int mmi=0x3f3f3f3f,mmx=-0x3f3f3f3f;
			if(fa<=fb)
			{
			int k = logg[fb-fa+1];
			mmi = min(fkminn[fa][k],fkminn[fb-(1<<k)+1][k]);
			mmx = max(fkmaxx[fa][k],fkmaxx[fb-(1<<k)+1][k]);
			}
			mmi = min(mmi,min(minhouzhui[l],minqianzhui[r]));
			mmx = max(mmx,max(maxhouzhui[l],maxqianzhui[r]));
			printf("%d\n",mmx-mmi);
		}
		else 
		{
			int mmi = 0x3f3f3f3f,mmx = -0x3f3f3f3f;
			for(int i=l;i<=r;i++) mmi=min(mmi,a[i]),mmx=max(mmx,a[i]);
			printf("%d\n",mmx-mmi);
		}
	} 
}

 

posted @ 2018-06-03 19:53  Newuser233  阅读(28)  评论(0)    收藏  举报