1. 程式人生 > >【bzoj5177】[Jsoi2013]貪心的導遊(分塊)

【bzoj5177】[Jsoi2013]貪心的導遊(分塊)

pro opened cstring n+1 比較 return 分享 time www.

  題目傳送門: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

【bzoj5177】[Jsoi2013]貪心的導遊(分塊)