zoukankan      html  css  js  c++  java
  • [学习笔记] rmq 问题

    0. 前言

    以前只会 (mathcal O(nlog n)-mathcal O(1)) 的朴素 ( m rmq)… 一度以为这就是 ( m rmq) 的全部…

    回想起来自己真是 ( m naive) 得可爱。

    放一道 ( m rmq) 的题:由乃救爷爷,之后的内容应该就在这题上测了…

    1. 前缀后缀分块

    1.1. 算法流程

    将序列划分成 (frac{n}{B}) 个块,对于每个 (i),预处理出它在所属块的前缀 (max) 与后缀 (max)。再对块与块之间求一个朴素 ( m rmq)

    查询就是出现散块就将前缀后缀拼在一起,整块就朴素 ( m rmq)。注意还有 (l,r) 在同一块内的情况,这个直接暴力查询。

    1.2. 复杂度分析

    不太好分析的是 (l,r) 在同一块内的情况。枚举块共 (frac{n}{B}) 种情况,那么 (l,r) 都在此块的概率为 (frac{B^2}{n^2}),所以总概率是 (frac{B}{n}),单次复杂度是 (mathcal O(frac{B^2}{n}))。所以总共是 (mathcal O(n+frac{n}{B}log frac{n}{B}+q+qcdot frac{B^2}{n}))

    (B) 至少为 (log n) 时,(frac{n}{B}log frac{n}{B}) 不超过 (n);当 (B) 至多为 (sqrt n) 时,(qcdot frac{B^2}{n}) 不超过 (n)

    所以 (B)(log nsim sqrt n) 时即可。还有一个 出题人心理学分析,还蛮有意思的。

    可以看作是 (mathcal O((n+q)cdot sqrt{log n})) 的。另外,听说块长设成 (2^k) 会快一些。

    1.3. 代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    } 
    
    namespace GenHelper {
        unsigned z1,z2,z3,z4,b;
        unsigned rand_() {
    	    b=((z1<<6)^z1)>>13;
    	    z1=((z1&4294967294U)<<18)^b;
    	    b=((z2<<2)^z2)>>27;
    	    z2=((z2&4294967288U)<<2)^b;
    	    b=((z3<<13)^z3)>>21;
    	    z3=((z3&4294967280U)<<7)^b;
    	    b=((z4<<3)^z4)>>12;
    	    z4=((z4&4294967168U)<<13)^b;
    	    return (z1^z2^z3^z4);
        }
    }
    
    void srand(unsigned x) {
    	using namespace GenHelper;
    	z1=x; z2=(~x)^0x233333333U;
    	z3=x^0x1234598766U; z4=(~x)+51;
    }
    
    int read() {
        using namespace GenHelper;
        int a=rand_()&32767;
        int b=rand_()&32767;
        return a*32768+b;
    }
    
    #include <iostream>
    using namespace std;
    
    const int B=64,maxn=20000005;
    
    int n,m,pre[maxn],suf[maxn];
    int st[20][maxn/B+2],bl[maxn];
    int lg[maxn/B+2],a[maxn];
    unsigned s;
    
    inline int Max(const int a,const int b) { return a>b?a:b; }
    
    int calc(int l,int r) {
    	if(l>r) swap(l,r);
    	int ret=0;
    	if(bl[l]^bl[r]) {
    		ret=Max(suf[l],pre[r]);
    		l=bl[l]+1,r=bl[r]-1;
    		if(l<=r) {
    			int d=lg[r-l+1];
    			ret=Max(ret,Max(st[d][l],st[d][r-(1<<d)+1]));
    		}
    	}
    	else {
    		for(int i=l;i<=r;++i)
    			ret=Max(ret,a[i]);
    	}
    	return ret;
    }
    
    void init() {
    	for(int i=1;i<=n;++i)
    		if((bl[i]-1)*B+1==i)
    			pre[i]=a[i];
    		else
    			pre[i]=Max(pre[i-1],a[i]);
    	suf[n]=a[n];
    	for(int i=n-1;i>=1;--i)
    		if(bl[i]*B==i)
    			suf[i]=a[i];
    		else
    			suf[i]=Max(suf[i+1],a[i]);
    	lg[0]=-1;
    	for(int i=1;i<=bl[n];++i)
    		lg[i]=lg[i>>1]+1,
    		st[0][i]=pre[i*B];
    	for(int j=1;(1<<j)<=bl[n];++j)
    		for(int i=1;i+(1<<j)-1<=bl[n];++i)
    			st[j][i]=Max(st[j-1][i],st[j-1][i+(1<<j-1)]);
    }
    
    int main() {
    	n=read(9),m=read(9),s=read(9);
    	srand(s);
    	for(int i=1;i<=n;++i)	
    		a[i]=read(),
    		bl[i]=(i-1)/B+1;
    	int l,r; init();
    	unsigned long long ans=0;
    	while(m--) {
    		l=read()%n+1;
    		r=read()%n+1;
    		ans+=calc(l,r);
    	}
    	print(ans,'
    ');
    	return 0;
    }
    

    2. 压位分块

    2.1. 算法流程

    (B) 设为 (log n),对块与块之间求一个朴素 ( m rmq)。对每个块从左到右维护递减的单调栈,注意由于将状态压缩成一个数字,我们要求字长 (wge B)。具体一点就是如果某个下标在单调栈内,就将数字对应那一位赋值为 (1)。查询块内 ((l,r)) 时就查询 (r) 对应的单调栈状态满足 (ige l) 的第一个为 (1) 的数字即可。可以用 __builtin_ctz()(mathcal O(1)) 内计算。

    2.2. 复杂度分析

    朴素 ( m rmq) 只用 (frac{n}{log n}) 个数,复杂度 (mathcal O(n))。单调栈预处理 (mathcal O(n)),查询 (mathcal O(q))

    但是模板题还不如前缀后缀分块跑得快就是了。可能是我人丑常数大吧。

    2.3. 代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    } 
    
    namespace GenHelper {
        unsigned z1,z2,z3,z4,b;
        unsigned rand_() {
    	    b=((z1<<6)^z1)>>13;
    	    z1=((z1&4294967294U)<<18)^b;
    	    b=((z2<<2)^z2)>>27;
    	    z2=((z2&4294967288U)<<2)^b;
    	    b=((z3<<13)^z3)>>21;
    	    z3=((z3&4294967280U)<<7)^b;
    	    b=((z4<<3)^z4)>>12;
    	    z4=((z4&4294967168U)<<13)^b;
    	    return (z1^z2^z3^z4);
        }
    }
    
    void srand(unsigned x) {
    	using namespace GenHelper;
    	z1=x; z2=(~x)^0x233333333U;
    	z3=x^0x1234598766U; z4=(~x)+51;
    }
    
    int read() {
        using namespace GenHelper;
        int a=rand_()&32767;
        int b=rand_()&32767;
        return a*32768+b;
    }
    
    inline int Max(int x,int y) {int m=(x-y)>>31; return y&m|x&~m;}
    
    #include <iostream>
    using namespace std;
    
    const int B=32,maxn=20000005;
    
    int n,m,l,r,S[B+5],tp;
    int a[maxn],lg[maxn/B+2];
    int st[20][(int)9e5];
    unsigned s;
    struct Stack {
    	int sta[B+5],delta;
    	
    	int QMax(int l,int r) {
    		return a[delta+l+__builtin_ctz(sta[r]>>l-1)];
    	}
    	
    	void ins(int *A) {
    		tp=0;
    		for(int i=1;i<=B;++i) {
    			sta[i]=sta[i-1];
    			while(tp and A[S[tp]]<=A[i])
    				sta[i]^=(1<<S[tp]-1),--tp;
    			S[++tp]=i,sta[i]^=(1<<i-1);
    		}
    	}
    } p[(int)9e5];
    
    void init() {
    	int len=(n-1)/B+1; lg[0]=-1;
    	for(int i=1;i<=len;++i) {
    		st[0][i]=a[(i-1)*B+1];
    		for(int j=(i-1)*B+2;j<=i*B;++j)
    			st[0][i]=Max(st[0][i],a[j]);
    		lg[i]=lg[i>>1]+1;
    		p[i].ins(a+(i-1)*B),p[i].delta=(i-1)*B;
    	}
    	for(int j=1;(1<<j)<=len;++j)
    		for(int i=1;i+(1<<j)-1<=len;++i)
    			st[j][i]=Max(st[j-1][i],st[j-1][i+(1<<j-1)]);
    }
    
    int rmq(int l,int r) {
    	int d=lg[r-l+1];
    	return Max(st[d][l],st[d][r-(1<<d)+1]);
    }
    
    int calc() {
    	int a=(l-1)/B+1,b=(r-1)/B+1,ret=0;
    	if(a+1<=b-1) ret=rmq(a+1,b-1);
    	if(a==b) ret=p[a].QMax(l-(a-1)*B,r-(a-1)*B);
    	else ret=Max(ret,Max(p[a].QMax(l-(a-1)*B,B),p[b].QMax(1,r-(b-1)*B)));
    	return ret;
    }
    
    int main() {
    	n=read(9),m=read(9),s=read(9);
    	srand(s);
    	for(int i=1;i<=n;++i)	
    		a[i]=read();
    	init();
    	unsigned long long ans=0;
    	while(m--) {
    		l=read()%n+1,r=read()%n+1;
    		if(l>r) swap(l,r);
    		ans+=calc();
    	}
    	print(ans,'
    ');
    	return 0;
    }
    

    3. (±1 ext{ rmq})

    听说时空都不太优,所以只放一个 别人的博客按理来说时间复杂度应该是 (mathcal O(n)-mathcal O(1)) 的。

    4. 笛卡尔树

    4.1. 算法流程

    建好笛卡尔树后(小根堆),如果要查询 ([l,r]) 的最小值就是求 (l,r) 在树上的 ( m lca)。然后可以暴力跳到 ( m lca)

    4.2. 复杂度分析

    由于在 随机数据 下,笛卡尔树的树高期望是 (log n) 的,所以应该是 (mathcal O(n)-mathcal O(log n))

  • 相关阅读:
    H5 移动端相册拍照 录音 录像,然后上传后台
    h5 移动端调用相机 摄像机 录音机获取文件,并下载到本地
    Java 判断字符串是否含有 数字 字母 特殊字符
    java 以0开头的数字计算,保留开头0
    Spring 与hibernate 整合,测试数据连接
    http://blog.csdn.net/basycia/article/details/52205916
    MySQL数据库基础知识002
    数据库基础知识001
    数组排序
    输出杨辉三角
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15134821.html
Copyright © 2011-2022 走看看