luogu P3793 由乃救爷爷
题面传送门
一种奇奇怪怪的rmq做法。依赖于数据随机。
首先对序列分\(S\)块,然后处理块内前后缀\(\max\),处理块间st表。
然后查询的时候块间\(st\)表\(O(1)\)查询,零散块前后缀\(\max\)查询。可以做到\(O(1)\)
但是有一个问题就是在同一块中没法做。只有暴力。
但是数据随机所以落到同一块内的概率是\(\frac{1}{S}\),然后均摊一下就是\(O(n)\)的。
\(S\)随便怎么取都可以。
code:
#include<cstdio>
#include<cmath>
#define N 20000039
#define W 10039
#define I inline
#define ull unsigned long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,s,x,y,a[N],q1[N],q2[N],st[W][20],w[W],lg[W],tot,pus;ull ans;
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;
}
I void swap(int &x,int &y){x^=y^=x^=y;}
I int find(int x,int y){int now=lg[y-x+1];return max(st[x][now],st[y-(1<<now)+1][now]);}
int main(){
freopen("1.in","r",stdin);
register int i,j,fr,en;scanf("%d%d%d",&n,&m,&s);srand(s);k=sqrt(n);
for(i=1;i<=n;i++)a[i]=read();
for(i=0;i<=n/k;i++){
fr=max(i*k,1);en=min(i*k+k-1,n);
for(q1[fr]=a[fr],j=fr+1;j<=en;j++) q1[j]=max(q1[j-1],a[j]);
for(q2[en]=a[en],j=en-1;j>=fr;j--) q2[j]=max(q2[j+1],a[j]);
w[i]=q1[en];
}
for(i=2;i<=n/k*2;i++) lg[i]=lg[i/2]+1;
for(i=n/k;~i;i--){
for(st[i][0]=w[i],j=1;i+(1<<j)-1<=n/k;j++) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
for(i=1;i<=m;i++){
x=read()%n+1;y=read()%n+1;(x>y)&&(swap(x,y),0);tot=0;
if(x/k==y/k)for(j=x;j<=y;j++) tot=max(tot,a[j]);
else pus=((x/k<y/k-1)?find(x/k+1,y/k-1):0),tot=max(pus,max(q2[x],q1[y]));ans+=tot;
}
printf("%llu\n",ans);
}