求个最大值
时间限制: 1 Sec 内存限制: 128 MB提交: 267 解决: 52
[提交][状态][讨论版]
题目描述
一直到 n,求满足 ai >= aj 的最大的 ai % aj。
输入
输出
样例输入
2
2 3
样例输出
1
提示
题解:
排序去重之后,对1到 max{ai}中的每个数字x求出小于x的最大 ai,记为 b[x]。 对于每个 ai 枚举它的倍数即可,时间复杂度 O(max{ai} + nlogn) 。
这题比赛里已经想到思路了,就是不知道怎么去表示。。 思路很简单,设a, b , a > b -> a/b = k....r(r是余数),这样我们肯定希望a离着k*b越元越好。也就是我们枚举倍数的时候(比赛中写过枚举倍数,但是n*n的。。)我们希望b*k离着a越远越好,但是不能远过b的大小,这样就难处理了,但是可以转化成 (k+x)*b离着a越近越好,那枚举到每个数的倍数时候,对他有用的就只有一个离着这个数最近的a【i】了、、前缀处理下就好了。。记录1-maxn*2每个数比他小但是最大的a[i]就行。。
这个问题中有几个点还是比较巧妙的!!!
(1)求的是a[j]>a[i] 且a[j]%a[i]的最大值,如果把研究a[j]的话问题是不好解决的,需要转化一下,也就是上面的这个转化过程,用倍数来想
(2)开始的时候我是感觉这样会超时的,因为开始的时候如果a[i]=2,这需要(200000*2)/2=200000的,然后每个数按照这种最坏的想,也就是200000*20000,这明显是会超时的,但是之后计算之后发现最大值是134565766,也就是说复杂度是1e7,不会超时的,其实这就是个调和级数计算方式(调和级数的值:n趋近与无穷后是:1/1+1/2+1/3...+1/n~ln(n+1) +r,r是欧拉常数,r=0.5772156649.),就是高数级数里面一个简单的概念好不好,都忘了。。还有这里其实就是放缩放错了,放缩方法分两种的好不好,张宇十八讲上好像看过,趋近于无穷的是不能简单用最大的放缩的。。。
(3)想要找到最接近a[j]的某个值,第一想法肯定是二分思想比较快,但是这里又是做了一个巧妙的思想,就是用了这个辅助数组,所以查找就直接是O(1)了,相当巧妙,学习了!!!
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; int b[maxn], a[maxn]; int main() { int n; while(~scanf("%d", &n)) { memset(b, 0, sizeof(b)); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); sort(a+1, a+1+n); int index = a[1]+1; a[n+1] = maxn*2; for(int i = 2; i <= n+1; i++) ///记录比j小的最大的a[i] while(index <= a[i]) b[index++] = a[i-1]; int ans = 0; for(int i = 1; i <= n; i++) for(int j = a[i]*2; j <= maxn*2; j += a[i]) ans = max(ans, b[j]%a[i]); printf("%d\n", ans) ; } return 0; }
还有网上有的说二分可以,按照上面复杂度计算是不可以的,并且竞赛之后发现已经回TL了!!!