题意:给一个数组A,求: Bi=maxi∤jAj , i≥2.Bi是在所有i不整除j下取得的最大值。
思路:首先:当j<i时肯定满足条件的,所以先考虑求一个pre的前缀最大值,再考虑大于i的情况:
此时j应当是在 [i*k+1,i*(k+1)-1]的闭区间内的最大值(k是正整数),先用ST表处理下数组内区间的最大值,然后查找区间的最大值就是O(1)了,类似于素数筛,我们可以枚举i的倍数,然后再从 [i*k+1,i*(k+1)-1]求得最大值。这样时间复杂度理论上可以达到O(n)。(ST表是求区间最大最小值非常快速的方法,比树状数组和线段树都要快)。最后就是要注意最后的 j 超过 i 的最大的倍数,而又小于n的情况就行了。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define siz 100005
#include <math.h>
using namespace std;
int gp[siz],pre[siz];
int n;
int B[siz];
int stmax[2*siz][30];
//int hp[siz];
void _Init()
{
int i,j;
for(i=1; i <= n; i++)
{
stmax[i][0]=gp[i];
}
for(j=1; j <= 17; j++)
{
for(i=1; i + (1<<j) - 1 <= n; i++)
{
stmax[i][j]=max(stmax[i][j - 1], stmax[i + (1 << (j - 1))][j - 1]);
}
}
}
void solve(){
int cnt = 1;
for(int i=2;i<=n;i++){
int ans = pre[i-1];
int ant;
int L,R,len;
// cout<<ans<<endl;
int j;
for(j=i+i;j<=n;j+=i){
L = j-i+1;
R = j - 1;
len = R-L+1;
//int mid = (L+R)>>1;
//cout<<L<<" "<<R<<endl;
int t= (int) (log(len*1.0)/log(2.0));
ant = max(stmax[L][t],stmax[R - (1<<t)+1][t]);
//cout<<ant<<"---"<<endl;
ans = max(ans,ant);
}
L = j-i+1;
len = n - L+1;
// cout<<L<<"***********8"<<endl;
if(L<=n){
int t= (int) (log(len*1.0)/log(2.0));
ant = max(stmax[L][t],stmax[n - (1<<t)+1][t]);
ans = max(ans,ant);
}
B[i] = ans;
}
printf("%d",B[2]);
for(int i=3;i<=n;i++){
printf(" %d",B[i]);
}
printf("\n");
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&gp[i]);
}
_Init();
pre[1] = gp[1];
for(int i=2;i<=n;i++){
pre[i] = max(pre[i-1],gp[i]);
}
solve();
}
return 0;
}