- 给定一个长度为(n)的序列,(m)次询问区间最大值。
- (n,mle2 imes10^7),数据随机
分块
考虑分块,维护(Mx_{i,j})表示第(isim j)个块的最大值,(pre_i)表示(i)在所在块中的前缀最大值,(suf_i)表示(i)在所在块中的后缀最大值。
那么一个询问就可以拆成一个后缀+一段完整块+一个前缀。
然而问题来了——如何处理块内询问?
考虑一次询问两个端点相同的概率是(O(frac1{sqrt n}))的,因此我们完全可以直接暴力。
代码:(O(n))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20000000
#define S 10000
using namespace std;
int n,m,s,a[N+5],sz,bl[N+5],Mx[S+5][S+5],pre[N+5],suf[N+5];
namespace R//随机数生成器
{
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) {z1=x,z2=(~x)^0x233333333U,z3=x^0x1234598766U,z4=(~x)+51;}
}
int read() {int a=R::rand_()&32767,b=R::rand_()&32767;return a*32768+b;}
int main()
{
RI i,j;for(scanf("%d%d%d",&n,&m,&s),R::srand(s),i=1;i<=n;++i) a[i]=read();
for(i=1;i<=n;++i) bl[i]=(i-1)/S+1,Mx[bl[i]][bl[i]]=max(Mx[bl[i]][bl[i]],a[i]);
for(i=1;i<=bl[n];++i) for(j=i+1;j<=bl[n];++j) Mx[i][j]=max(Mx[i][j-1],Mx[j][j]);//第i~j个块的最大值
for(i=1;i<=n;++i) pre[i]=max(bl[i-1]^bl[i]?0:pre[i-1],a[i]);//i所在块的前缀最大值
for(i=n;i>=1;--i) suf[i]=max(bl[i+1]^bl[i]?0:suf[i+1],a[i]);//i所在块的后缀最大值
RI l,r,t;unsigned long long ans=0;W(m--)
{
(l=read()%n+1)>(r=read()%n+1)&&(swap(l,r),0);
if(bl[l]==bl[r]) {for(t=0,i=l;i<=r;++i) t=max(t,a[i]);ans+=t;}//同块直接暴力
else ans+=max(Mx[bl[l]+1][bl[r]-1],max(suf[l],pre[r]));//不同块利用维护出的信息求解
}return printf("%llu
",ans),0;
}