cf 484b 二分搜索/区间最值

B. Maximum Value
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a sequence a consisting of n integers. Find the maximum possible value of  (integer remainder of ai divided by aj), where 1 ≤ i, j ≤ n and ai ≥ aj.

Input

The first line contains integer n — the length of the sequence (1 ≤ n ≤ 2·105).

The second line contains n space-separated integers ai (1 ≤ ai ≤ 106).

Output

Print the answer to the problem.

Sample test(s)
input
3
3 4 5
output
2

找出满足a[i]>=a[j]的a[i]%a[j]的最大值。

这题数据有个特点,ai <= 10^6。所以我想到了对于aj,找出mod它得到最大的值,就可以循环找出[k*aj+1,k*aj+aj-1]区间中出现过的最大值。由于查询的次数非常多,所以用st算法查询区间最大值会好很多。另外有几个地方可以做一些优化。对数据可以进行离散化,去除重复的,减少重复查询的时间。查询最大值时需要log(b-a+1)/log(2),由于查询很多次所以预处理出1-10^6所有的log值会快很多。st查询函数前加inline也会快很多 (没加inline前904ms,加了685ms)

看了别人的解题报告,发现有比较简洁的做法。。。枚举aj的倍数,二分搜索找出小于aj的倍数的最大值即可。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

#define maxn 200005
int n;
int a[maxn];
int h[5*maxn];
int maxi;
double log1[5*maxn];

int stmax[5*maxn][21];

void get_st()
{
    for(int i = 1; i <= maxi; i++)
        stmax[i][0] = h[i];

    for(int j = 1; (1<<j) <= maxi; j++)
        for(int i = 1; i+(1<<j)-1 <= maxi; i++)
        stmax[i][j] = max(stmax[i][j-1], stmax[i+(1<<(j-1))][j-1]);
}

inline int get_max(int a, int b)
{
    int j = log1[b-a+1]/log1[2];
    return max(stmax[a][j], stmax[b-(1<<j)+1][j]);
}


int main()
{
    for(int i = 1; i < 5*maxn; i++)
        log1[i] = log(i);

    while(scanf("%d", &n)!=EOF){
        for(int i = 0; i < n; i++)
            scanf("%d", a+i);
        sort(a, a+n);
        n = unique(a, a+n)-a;
        maxi = a[n-1];

        memset(h, 0, sizeof(h));
        for(int i = 0; i < n; i++)
            h[a[i]] = a[i];

        get_st();
        int ans = 0;
        for(int i = 0; i < n; i++){
            for(int l = a[i]+1, r = 2*a[i]-1; l <= r && l <= maxi && ans < a[i]-1; l+=a[i], r+=a[i])
                ans = max(ans, get_max(l,min(r,maxi))-l+1);
        }

        printf("%d\n", ans);
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值