zoukankan      html  css  js  c++  java
  • P3793由乃救爷爷【分块,ST表】

    正题

    题目链接:https://www.luogu.com.cn/problem/P3793


    题目大意

    给出\(n\)个数字的一个序列\(m\)次询问区间最大值

    保证数据随机

    \(1\leq n,m\leq 2\times 10^7\)


    解题思路

    使用\(ST\)表可以做到\(O(1)\)询问,但是预处理的时空复杂度都是\(O(n\log n)\),且自带大常数导致过不了。

    如何加快预处理的时间,(因为是lxl的题目所以)考虑使用分块。每次询问可以分为整块的部分和不是整块的零散部分。

    去掉没有跨块的情况,那么零散的部分就是块内前后缀最大值。然后整块的部分用\(ST\)表就好了。

    那么没有跨块的情况是不是还需要给每个块维护一个\(ST\)表?这样空间还是过不了,其实可以考虑将没有跨块的情况按顺序每个块每个块离线处理,这样就可以过了。

    但是数据保证随机,所以随机到同一个块内的概率是\(\frac{1}{T}\),也就是期望\(\sqrt n\)次,暴力处理是\(O(\sqrt n)\)的,所以直接暴力处理就可以了

    时间复杂度\(O(n\log\sqrt n+m)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=2e7+10;
    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;
    }
    int n,m,a[N],lg[N],p[N],q[N],g[5000][13];
    int L[5000],R[5000],pos[N];
    unsigned s;
    unsigned long long ans;
    int AskT(int l,int r){
    	if(l>r)return 0;int z=lg[r-l+1];
    	return max(g[l][z],g[r-(1<<z)+1][z]);
    }
    int main()
    {
    	scanf("%d%d%u",&n,&m,&s);
    	srand(s);
    	for(int i=1;i<=n;i++)a[i]=read();
    	int T=sqrt(n);
    	for(int i=1;i<=T;i++)
    		L[i]=R[i-1]+1,R[i]=i*T;
    	if(R[T]!=n)++T,L[T]=R[T-1]+1,R[T]=n;
    	for(int i=1;i<=T;i++){
    		for(int j=L[i];j<=R[i];j++)pos[j]=i,g[i][0]=max(g[i][0],a[j]);
    		p[L[i]]=a[L[i]];q[R[i]]=a[R[i]];
    		for(int j=L[i]+1;j<=R[i];j++)p[j]=max(p[j-1],a[j]);
    		for(int j=R[i]-1;j>=L[i];j--)q[j]=max(q[j+1],a[j]);
    	}
    	for(int j=1;(1<<j)<=T;j++)
    		for(int i=1;i+(1<<j)-1<=T;i++)
    			g[i][j]=max(g[i][j-1],g[i+(1<<j-1)][j-1]);
    	for(int i=2;i<=T;i++)lg[i]=lg[i>>1]+1;
    	for(int i=1;i<=m;i++){
    		int l=read(),r=read();
    		l=l%n+1;r=r%n+1;
    		if(l>r)swap(l,r);
    		int x=pos[l],y=pos[r];
    		int tmp=0;
    		if(x==y){
    			for(int i=l;i<=r;i++)
    				tmp=max(tmp,a[i]);
    		}
    		else{
    			tmp=AskT(x+1,y-1);
    			tmp=max(tmp,max(q[l],p[r]));
    		}
    		ans+=tmp;
    	}
    	printf("%llu\n",ans);
    }
    
  • 相关阅读:
    给Linux添加google搜索命令
    安卓设置脚本开机自启动
    重读The C programming Lanuage 笔记四:c预处理
    重读The C programming Lanuage 笔记三:简单计算器程序
    重读The C programming Lanuage 笔记二:运算符优先级
    重读The C programming Lanuage 笔记一:类型转换
    【 转】 C/C++结构体和联合体的区别
    python 柯里化 和装饰器
    qq bug 一个
    网站收集
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14526308.html
Copyright © 2011-2022 走看看