【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;
}
bzoj5177

 

posted @ 2019-04-10 19:48  QuartZ_Z  阅读(175)  评论(0编辑  收藏  举报