L.Mod
题目大意
给定一个序列和 l,r,kl,r, kl,r,k,询问 [l,r][l,r][l,r] 中的数字 mod kkk 的最大值,1≤n,k≤500001\leq n,k\leq 500001≤n,k≤50000
分块,预处理fi,jf_{i,j}fi,j表示第iii块mod jjj的最大值。
对于每一块,维护 mximx_imxi, 表示本块内所有出现过的数字中不大于iii的最大的数字。
对于fi,jf_{i,j}fi,j,依次枚举长度为jjj的区间[k×j,k×j+j−1][k×j,k×j+j-1][k×j,k×j+j−1],于是有
fi,j=maxk{mxk×j+j−1−k×j}f_{i,j}=\max_{k}\{\text{mx}_{k×j+j-1}-k×j\}fi,j=kmax{mxk×j+j−1−k×j}
设块的大小为B\text BB
时间复杂度为O{NBNlogN+M(B+NB)}O\{\frac N{\text B}N\log N+M(\text B+\frac N{\text B})\}O{BNNlogN+M(B+BN)}
取B=N+N2logNM\text B=\sqrt{N+\frac{N^2\log N}{M}}B=N+MN2logN最优
Code
此代码并未经过验证,无意间看到之前校赛的题目,貌似已经没有oj去测评。。。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg;
}
const int N=50010;
int n,m;
int a[N];
int b[N],Bs;
int f[400][N],mx[N];
void prework()
{Bs=sqrt(10*n);for(int i=1;i<=n;i++) b[i]=(i-1)/Bs+1;for(int i=1;i<b[n];i++){for(int k=(b[i]-1)*Bs+1;k<b[i]*Bs;k++) mx[a[k]]=a[k];memeset(mx,0,sizeof mx);for(int j=1;j<=50000;j++) mx[j]=max(mx[j],mx[j-1]);for(int j=1;j<=50000;j++)for(int k=1;k*j+j-1<=50000;k++)f[i][j]=max(f[i][j],mx[k*j+j-1]-k*j);}
}
int query(int l,int r,int v)
{int ans=0;for(int i=l;i<=min(r,Bs*b[l]);i++) ans=max(ans,a[i]%v);if(b[l]!=b[r]) for(int i=(b[r]-1)*Bs+1;i<=r;i++) ans=max(ans,a[i]%v);for(int i=b[l]+1;i<b[r];i++) ans=max(ans,f[i][v]);return ans;
}
int main()
{n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();prework();while(m--) {int l=rd(),r=rd(),k=rd();printf("%d\n",query(l,r,k));}return 0;
}