【bzoj5177】[Jsoi2013]贪心的导游(分块)
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=5177
在网上看到的题解基本都是用主席树,也就是带点骚操作的暴力直接艹过去的。这里分享一个比较清真的分块做法。
因为$ p , a_i $的值域较小,因此我们可以考虑把序列分成$ size $块,预处理出每个块内的数模每个$ p $的最大值,那么查询时只需查询区间中的整块答案,然后再统计零散的元素的贡献。预处理时,我们可以考虑对当前块维护一个数组$ last[i] $表示在当前块内出现过的小于等于$ i $的最大的数,在计算模$ p $的答案时,因为$ f(x)=x \mod p $是一个分段的单调函数,在$ [kp,(k+1)p-1] (k \in \mathbb{Z})$上单调递增,因此我们只需对每个区间$ [kp,(k+1)p-1] $求出区间内的最大值,也就是$ last[(k+1)p-1] $,作为模$ p $值最大的数。因此,每个块的预处理复杂度为$ O(1000 \ln 1000) $。
总时间复杂度为$ O(n+1000 \ln 1000size+m(size+\frac{n}{size})) $,当取$ size=\sqrt{n} $时时间复杂度为$ 1000 \ln 1000 \sqrt{n}+m\sqrt{n}) \approx 5.7 \times 10^7 $,比那些$ O(1000 m \log n) $的做法清真到不知道哪里去了。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<set> #define ll long long #define mod 1000000007 #define Mod1(x) (x>=mod?x-mod:x) #define Mod2(x) (x<0?x+mod:x) #define maxn 1000010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static int buf[20],len; len=0; if(x<0)x=-x,putchar('-'); for(;x;x/=10)buf[len++]=x%10; if(!len)putchar('0'); else while(len)putchar(buf[--len]+'0'); } inline void writeln(ll x){write(x); putchar('\n');} inline void writesp(ll x){write(x); putchar(' ');} int mx[1010][1010],last[1010]; int a[maxn]; int n,m,size; int main() { // freopen("bzoj5177.in","r",stdin); // freopen("bzoj5177.out","w",stdout); n=read(); m=read(); for(int i=0;i<n;i++) a[i]=read(); size=sqrt(n); // writeln(size); for(int k=0;k*size<n;k++){ memset(last,0,sizeof(last)); for(int i=k*size;i<(k+1)*size&&i<n;i++) last[a[i]]=a[i]; for(int i=1;i<=1000;i++) if(!last[i])last[i]=last[i-1]; for(int i=1;i<=1000;i++){ for(int j=i;j<=1000;j+=i) mx[k][i]=std::max(mx[k][i],last[j-1]-(j-i)); mx[k][i]=std::max(mx[k][i],last[1000]%i); } } while(m--){ int l=read(),r=read(),p=read(); if(l>r){ int tmp=l; l=r; r=tmp; } int idl=l/size,idr=r/size; if(idl==idr){ int ans=0; for(int i=l;i<=r;i++) ans=std::max(ans,a[i]%p); writeln(ans); } else{ int ans=0; for(int i=idl+1;i<idr;i++) ans=std::max(ans,mx[i][p]); for(int i=l;i<(idl+1)*size;i++) ans=std::max(ans,a[i]%p); for(int i=idr*size;i<=r;i++) ans=std::max(ans,a[i]%p); writeln(ans); } } // fclose(stdin); fclose(stdout); return 0; }