压位单调栈实现线性 rmq

压位单调栈实现线性 rmq

还是分块 st 表(令块长为 \(B\)),不同的是块内的操作。我们预处理出每个块的前后缀最值,于是要解决的就只有 \(l,r\) 均落在同一块内的情况。有一个单调栈做法是处理出每个前缀的单调栈情况(存储下标),于是询问 \([l,r]\) 就变成了在 \(r\) 所对应的前缀的单调栈里二分查找第一个大于等于 \(l\) 的元素。

这样做的复杂度显然是不对的,但我们可以把每个单调栈压成 bitset, \(stk_i=1\) 就代表着当前单调栈里有 \(i\) 这个元素。于是 .back() 就等价于 highbit,其余操作也可以用相应的位运算表示。唯一麻烦的是二分,这个似乎有一些奇妙的科技能使得它做到 \(O(1)\),但其实 \(\log(B)\)\(O(1)\) 也差不了多少,不是吗(。upd:可以打表 \(16\) 位预处理

namespace rmq{
const int N=2e6+5,inf=1e9;
namespace Table{
	int n,lg[N];
	pii st[N/B+2][20];
	//Table(){fo(i,0,N/B+1) st[i][0]=mk(inf,-1);} 
	void init(){
		fo(i,2,n) lg[i]=lg[i>>1]+1;
		fo(j,1,lg[n])
			fo(i,1,n) st[i][j]=min(st[i][j-1],st[min(i+(1<<(j-1)),n)][j-1]);
	}
	pii query(int l,int r){int g=lg[r-l+1];return min(st[l][g],st[r-(1<<g)+1][g]);} 
}
int n,top,bit[1<<(B/2+1)],stk[B+1],bel[N];
pii a[N],pre[N/B+2][B+1],suf[N/B+2][B+1];
unsigned int wyx[N],pw[B+1];
void init(){--n;
	//预处理位运算相关信息 
	fo(i,0,n) a[i]=a[i+1];
	pw[0]=1;fo(i,1,B) pw[i]=pw[i-1]<<1;
	fo(i,1,(1<<(B/2))-1) bit[i]=(i&1)?0:(1+bit[i>>1]);
	//cout<<"bit:";fo(i,1,16)cout<<bit[i]<<' ';puts("");
	
	//预处理块与块间的 st 表 
	fo(i,1,n/B+1) Table::st[i][0]=mk(inf,-1);
	fo(i,0,n) bel[i]=i/B+1,sml(Table::st[bel[i]][0],a[i]);
	int T=bel[n];Table::n=T;Table::init();
	
	//预处理块内单调栈,用 unsigned int 压缩信息 
	fo(i,1,T){
		int qwq=(i-1)*B;
		pre[i][0]=a[qwq];fo(j,1,B-1) pre[i][j]=min(pre[i][j-1],a[qwq+j]);
		suf[i][B-1]=a[qwq+B-1];go(j,B-2,0) suf[i][j]=min(suf[i][j+1],a[qwq+j]);
		top=1;wyx[qwq]=1;//wyxqwq
		stk[top]=qwq;
		fo(j,qwq+1,qwq+B-1){
			wyx[j]=wyx[j-1];
			while(top&&a[stk[top]]>=a[j]) wyx[j]^=pw[stk[top--]-qwq];
			stk[++top]=j;wyx[j]^=pw[j-qwq];
			//printf("%d:",j);out(stk,1,top);
			//cout<<"压位:";fo(k,0,B-1) if(wyx[j]>>k&1) cout<<qwq+k<<' ';puts("");
		}
	}
}
int first_bit(unsigned int x){//unsigned int!! 
	if(x&(pw[B/2]-1)) return bit[x&(pw[B/2]-1)];
	return B/2+bit[x>>(B/2)];
}
pii query(int l,int r){
	l--,r--;
	if(bel[l]==bel[r]) return a[l+first_bit(wyx[r]>>(l-(bel[l]-1)*B))];
	else{
		pii mn1=suf[bel[l]][l-(bel[l]-1)*B],mn2=pre[bel[r]][r-(bel[r]-1)*B];
		if(bel[l]+1==bel[r]) return min(mn1,mn2);
		return min(min(mn1,mn2),Table::query(bel[l]+1,bel[r]-1));
	}
}
}
posted @ 2021-10-08 20:50  真正的菜鸡vectorwyx  阅读(274)  评论(1)    收藏  举报