您的位置:首页 > 产品设计 > UI/UE

codeforces 484 B Maximum Value 排序+二分 + 数学

2015-09-06 14:26 423 查看
链接:http://codeforces.com/contest/484/problem/B

题意:给定长度为n的序列,要求序列中 ai % aj 最大的值是多少。

思路:

给定的n为10510^5,直接暴力O(n2)O(n^2)肯定是不行的。

首先可以明确,aj如果越大的话,那么取模后的值就可能越大,因为取模后只能在0~aj -1 这些数之中了。

又因为在这道题中,序列的顺序并没有用,所以考虑进行排序,从小到大,然后从大的开始取。

对于同一个aj的话要使取模后的数尽可能的大的话,ai应该是出现在小于aj的某个倍数的第一个数。那么我们就可以在序列中二分查找大于等于 aj k倍的数,以及小于k+1 倍 的第一个数。(查找k倍,防止少了模为0的情况)

确定算法,从大到小枚举序列中的元素,从一倍开始,每次都二分查找。如果已经在这个序列中找不到大于等于 k * aj 的数,那么就停止,枚举下一个元素。但是发现这样可能会超时,因为如果这个序列中只有很少的元素,但是这些数直接差距非常大,比如1 100000 ,那么这样枚举倍数的话就一定会枚举到100000上,但是会发现从2~100000找到的都是100000这个数,都是重复的。

所以考虑直接用迭代这个倍数,如果找到第一个大于等于k*aj的数 ap,那么将 j 变为ap/aj + 1,防止下次又找到ap这个数。

[code]#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1000009
#define INF 0x3f3f3f3f
int a[M];
int n;
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n)==1)
    {
        for(int i = 0;i< n;i++)
            scanf("%d",&a[i]);
        sort(a,a+n);
        int ans = -INF;
        for(int i = n-1;i >= 0;i--)
        {
            for(int j = 1;;)
            {
                int p = lower_bound(a+i,a+n,j*a[i]) - a;
                int q = lower_bound(a+i,a+n,(j+1)*a[i]) - a - 1;
                if(p == n) break;
                ans = max(a[p]%a[i],ans);
                ans = max(a[q]%a[i],ans);
                j = a[p] / a[i] + 1;
                //直接迭代到有用的倍数上,避免出现 1 1000 这样无用循环太多的情况
            }
            if(ans > a[i]) break;
        }
        printf("%d\n",ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: