noip2018 雅禮模擬賽day1 T1
阿新 • • 發佈:2018-12-16
分塊...
首先我們可以看到,對於任意的模數k,所有的可能答案一定會在所有k的整數倍的前驅處取到
證明:假設所有值均小於k,那麼我們顯然答案是這其中的最大值
假設所有值均小於2k,那麼我們將這些值分成兩部分,小於k的部分中的最大值即為取模k的最大值,而在[k,2k]之間的部分取模k即相當於-k,所以與2k最接近的值即為取模k的最大值,兩者取最大即為整體取模k的值
那麼我們對整個序列進行分塊,對每個塊預處理出對每個所有k取模的最大值,這樣在查詢時塊內的部分O(1)即可
而再根據上面的思想,我們可以知道:在處理最大值時,只需找出k的所有整數倍,求出在塊中小於他的最大值,然後對所有這樣的值取模k比較即可
這樣時間複雜度即為調和級數求和,接近klnk
然後查詢即可,將分塊大小設為1000能獲得最佳時間複雜度,但常數巨大,必須開O2
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; const int siz=999; int a[100005]; int f[205][100005]; int temp[100005]; int n,m; inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { freopen("flower.in","r",stdin); freopen("flower.out","w",stdout); n=read(),m=read(); for(int i=1;i<=n;i++) { a[i]=read(); } int cnt=(n-1)/siz+1; for(int i=1;i<=cnt;i++) { memset(temp,0,sizeof(temp)); for(int j=siz*(i-1)+1;j<=min(siz*i,n);j++) { temp[a[j]]=a[j]; } for(int j=1;j<=100001;j++) { temp[j]=max(temp[j],temp[j-1]); } for(int j=1;j<=100001;j++) { for(int k=0;k<=100001;k+=j) { f[i][j]=max(f[i][j],temp[min(k+j-1,100001)]-k); } } } for(int i=1;i<=m;i++) { int l=read(),r=read(),k=read(); int lq=(l-1)/siz+1; int rq=r/siz; int ret=0; if(rq<=lq+1) { for(int j=l;j<=r;j++) { ret=max(ret,a[j]%k); } }else { for(int j=l;j<=lq*siz;j++) { ret=max(ret,a[j]%k); } for(int j=rq*siz;j<=r;j++) { ret=max(ret,a[j]%k); } for(int j=lq+1;j<=rq;j++) { ret=max(ret,f[j][k]); } } printf("%d\n",ret); } return 0; }