【洛谷3793】由乃救爷爷(分块)

点此看题面

  • 给定一个长度为(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;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3793.html