2017多校训练6-1003:Inversion

本文介绍了一种高效算法,用于解决给定数组中特定条件下的区间最大值问题。通过预处理前缀最大值和使用ST表来加速区间查询,实现了接近O(n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给一个数组A,求: Bi=maxijAj , i2.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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值