题干:
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.
Examples
Input
3
3 4 5
Output
2
解题报告:
拿到题,涉及取模操作,要知道可肯定和因子有关,因为a%m我可以认为是模掉了(a减去了)一个m,两个m,三个m。。等等。这题就是利用这个原理,我们先把他离散化到数组中,然后枚举1~1e6(相当于去重了),如果当前数字出现过,那就枚举他的倍数。。。所以我们需要预处理一个bk数组,使得对于x,可以O(1)求出小于x的最大的数字。要记得处理数组的时候需要处理到2 * 1e6。因为你枚举是刚开始就 i + i 了,细节要注意。
实际思考的思路实际是这样:
然后考虑对于Ai来讲,其能够找到的Aj如果是Ai的因子,那么一定Ai%Aj==0.如果此时Aj是Ai的最大因子数(不包括Ai本身),那么A[ i ]%A[ j + 1 --->i-1]是会逐渐递减的,所以那么我们每一次找到一个因子数,那么期望的最大模值肯定要在A[ i ]%A[ j + 1 ]中选取,所以我们可以考虑O(n)枚举Ai然后sqrt(A[i])去枚举因子数,然后过程维护一个最大值即可。总时间复杂度O(n*sqrt(n)).
我们刚刚是在枚举因子数,所以还要话sqrt(n)的时间去算因子,反过来想,我们不妨枚举倍数。那么我们需要枚举1e6+1e6/2+1e6/3+1e6/4+.............1e6/1e6次,也就是logn的时间复杂度。(也就是 对于a%m,刚开始我们是枚举a然后找对应的m,再把m改大一点点,维护最大值。现在变成a%m去枚举m,然后找对应的a,再把a改小一点点,维护最大值。)
那么我们枚举一个数之后,枚举其倍数,对应在数组中找到比这个倍数小的最大的数,那么ans=max(ans,这个倍数小的最大的数%枚举出来的这个数);
实现方式可以二分,这样复杂度多一个log,我们也可以先预处理出来答案然后O(1)查询,详情见代码。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e6 + 5;
ll a[MAX],ans,maxx;
int bk[MAX];
const ll INF = 0x3f3f3f3f3f;
int main() {memset(bk,-1,sizeof bk);int n;cin>>n;for(int i = 1; i<=n; i++) scanf("%lld",a+i) , bk[a[i]] = a[i] , maxx = max(maxx,a[i]);sort(a+1,a+n+1);for(int i = 1; i<=2000050; i++) {if(bk[i] == -1) bk[i] = bk[i-1];}for(int i = 1; i<=1000050; i++) {if(bk[i] != i) continue;for(int j = 2*i; j<=2000050; j+=i) {if(bk[j-1] > i) ans = max(ans,1ll*bk[j-1]%i);}}printf("%lld\n",ans);return 0;
}
AC代码2:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e6 + 5;
ll a[MAX],ans,maxx;
int bk[MAX];
const ll INF = 0x3f3f3f3f3f;
int main() {memset(bk,-1,sizeof bk);int n;cin>>n;for(int i = 1; i<=n; i++) scanf("%lld",a+i) , bk[a[i]] = a[i] , maxx = max(maxx,a[i]);sort(a+1,a+n+1);for(int i = 1; i<=1000050; i++) {if(bk[i] == -1) bk[i] = bk[i-1];}for(int i = 1; i<=1000050; i++) {if(bk[i] != i) continue;for(int j = i; j<=1000050; j+=i) {if(bk[j-1] > i) ans = max(ans,1ll*bk[j-1]%i);if (bk[1000050] > i) {ans = max(ans, 1ll*bk[1000050] % i);}}}printf("%lld\n",ans);return 0;
}
/*
2
1000000 999999
*/
TLE代码:
for(int i = 1; i<=n; i++) {for(int j = 2*a[i]; j<=2000050; j+=a[i]) {if(bk[j-1] > a[i]) ans = max(ans,bk[j-1]%a[i]);}}
不能这么写,,,如果是2e5个1,分分钟给你卡成n*2.。(当然你先去重就另说了)
所以看一个题,不能直接看数据范围算复杂度,还需要看实际实现代码的姿势的最差复杂度,或者是否有特殊样例可以卡T,避免被卡。