考虑把询问的公差大的和小的分别考虑,大的直接暴力,对于小的公差,我们每个公差建一个1~N的线段树,维护1,1+i,1+2i...2,2+i,..3,3+i上的权值,这样一个等差序列就转换成一段区间,线段树上查就好了
讲道理这个算法取公差按根号分最优,复杂度是n sqrt n log n的……但是我们按5分,然后很快就过去了……
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 70010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
int n,m;
int a[MAXN];
int p[6][MAXN],np[6][MAXN];
struct seg{
int c;
int v[MAXN<<2];
inline void ud(int x){
v[x]=max(v[x<<1],v[x<<1|1]);
}
void build(int x,int y,int z){
if(y==z){
v[x]=a[np[c][y]];
return ;
}
int mid=y+z>>1;
build(x<<1,y,mid);
build(x<<1|1,mid+1,z);
ud(x);
}
void change(int x,int y,int z,int p){
if(y==z){
v[x]=a[np[c][y]];
return ;
}
int mid=y+z>>1;
if(p<=mid){
change(x<<1,y,mid,p);
}else{
change(x<<1|1,mid+1,z,p);
}
ud(x);
}
int ask(int x,int y,int z,int l,int r){
if(y==l&&z==r){
return v[x];
}
int mid=y+z>>1;
if(r<=mid){
return ask(x<<1,y,mid,l,r);
}else if(l>mid){
return ask(x<<1|1,mid+1,z,l,r);
}else{
return max(ask(x<<1,y,mid,l,mid),ask(x<<1|1,mid+1,z,mid+1,r));
}
}
};
seg T[6];
int main(){
int i,j,o,x,y;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(i=1;i<=5;i++){
int now=1;
int st=1;
for(j=1;j<=n;j++){
p[i][now]=j;
np[i][j]=now;
now+=i;
if(now>n){
now=++st;
}
}
T[i].c=i;
T[i].build(1,1,n);
}
scanf("%d",&m);
while(m--){
scanf("%d%d%d",&o,&x,&y);
if(o==0){
a[x]+=y;
for(i=1;i<=5;i++){
T[i].change(1,1,n,p[i][x]);
}
}
if(o==1){
if(y<=5){
int r=p[y][x]+(n-x)/y;
printf("%d\n",T[y].ask(1,1,n,p[y][x],r));
}else{
int ans=a[x];
for(i=x;i<=n;i+=y){
ans=max(ans,a[i]);
}
printf("%d\n",ans);
}
}
}
return 0;
}
/*
10
1 6 1 4 9 4 8 2 8 5
10
1 3 3
0 5 4
0 3 8
1 2 5
1 4 8
1 7 5
1 3 6
0 1 2
1 5 3
1 4 9
*/