养花
题目描述
小(C)在家种了(n)盆花,每盆花有一个艳丽度(a_i).
在接下来的(m)天中,每天早晨他会从一段编号连续的花中选择一盆摆放在客厅, 并在晚上放回. 同时每天有特定的光照强度(k_i), 如果这一天里摆放在客厅的花艳丽度为(x), 则他能获得的喜悦度为(x mathrm{mod} k_i).
他希望知道, 每一天他能获得的最大喜悦度是多少.
输出输出格式
输入格式
数据第一行包含两个正整数(n,m).
接下来一行(n)个正整数, 第(i)个数(a_i)表示第(i)盆花的艳丽度.
接下来(m)行, 每行三个正整数(l_i,r_i,k_i), 表示选花区间和光照强度.
输出格式
输出(m)行, 每行一个整数, 表示最大喜悦度.
说明
对于(20\%)的数据,(n,mle 4000).
对于(40\%)的数据,(n,mle 50000).
对于另外(20\%)的数据,(a_ile 300).
对于(100\%)的数据,(n,m,a_i,k_ile 10^5).
区间询问 (a_i mod k) 的最大值
如果分块搞得熟估计可以A掉吧,或者主席树敢瞎写之类的
最开始想到一个垃圾的(nlognsqrt n)做法
思路是(k le sqrt n)预处理线段树套分块,然后(logn)查询
(k > sqrt n)时暴力在主席树上找,单次(sqrt n logn)
40pts 都拿不到
正解的做法是分块,对位置的分块
设块大小为(S),每个块预处理出模所有(k)时的最值
处理的技巧其实有点神奇,(f[i])表示当前块(i)这个值的位置前面一个值是多少
然后枚举(k),枚举每个区间,用(f[i])更新
对每个块的复杂度是(sum_{i=1}^n frac{n}{i})
发现是调和级数,则为(nlnn)
总复杂度:(O(n^2lnn/s+ms))
取(S=sqrt{nlnn}),得到复杂度(O(nsqrt{nlnn}))
事实上上界比较满,常数也不小,你谷最慢点跑了1.5s(雅礼只开了1s,跑的真快emm
Code:
#include <cstdio>
#include <cstring>
const int N=1e5;
const int S=1e3;
const int M=1e2;
int mx[M+2][N+10],n,m,a[N+10],f[N+10];
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",a+i);
int num=(n-1)/S;
for(int i=0;i<=num;i++)
{
memset(f,0,sizeof(f));
int l=i*S+1,r=min(n,(i+1)*S);
for(int j=l;j<=r;j++) f[a[j]]=a[j];
for(int j=1;j<=N;j++) f[j]=max(f[j-1],f[j]);
for(int j=1;j<=N;j++)
for(int k=0;k<=N;k+=j) mx[i][j]=max(mx[i][j],f[min(k+j-1,N)]-k);
}
for(int l,r,k,i=1;i<=m;i++)
{
int ans=0;
scanf("%d%d%d",&l,&r,&k);
int ll=(l-1)/S,rr=(r-1)/S;
for(int j=ll+1;j<rr;j++) ans=max(ans,mx[j][k]);
for(int j=l;j<=min(r,(ll+1)*S);j++) ans=max(ans,a[j]%k);
for(int j=max(l,rr*S+1);j<=r;j++) ans=max(ans,a[j]%k);
printf("%d
",ans);
}
return 0;
}
2018.10.1