[省选联考 2021 B 卷] 取模

给出个数组\(a\),选择不同的三个下标\(i,j,k\),最大化\((a_i+a_j)\mod a_k\)

\(n\le 2*10^5\)


\(a_i\)排序。

先讲暴力:枚举模数\(a_k\),令\(b_i=a_i \mod a_k\)。分成两类:\(b_i+b_j\ge a_k\)\(b_i+b_j<a_k\)。其中,前者只需要算\((a_{k-1}+a_{k-2})\mod a_k\)。证明:假如存在\(p>k\),且\(a_{p} \mod a_k>a_{k-2}\),则有\(a_{k-2}+a_k<a_p\),此时选择\((a_{k-2}+a_k)\mod a_p\)更优。后者可以求出\(b_i\)之后,排序,双指针计算。于是就做到了\(O(n^2\lg n)\)的复杂度。

考虑优化这个暴力:先算\(ans=\max ((a_{k-2}+a_{k-1})\mod a_k)\)。然后\(k\)从后往前枚举,如果\(a_k-1>ans\)则以\(a_k\)为模数计算上面后面的第二类。

显然要考虑的\(k\)是一段后缀。下面证明只有\(O(\log V)\)个:

假如最终的\(ans\)满足\(ans<a_s\le a_{s+1}\le \dots \le a_n\)

根据第一类,有:\(\forall i\in[s,n-2],ans\ge (a_i+a_{i+1})\mod a_{i+2}\ge a_i+a_{i+1}-a_{i+2}\)。于是有\(a_{i+2}-ans\ge (a_{i}-ans)+(a_{i+1}-ans)\)。令\(c_i=a_i-ans\)\(c_i>0\),且\(c_i\)至少是类似斐波拉契数列增长的,而且\(c_n=a_n-ans=O(V)\),于是长度为\(O(\log V)\)级别。

总时间\(O(n\log n\log V)\)


using namespace std;
#include <bits/stdc++.h>
#define N 200005
int n;
int a[N],b[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	int ans=0;
	for (int i=3;i<=n;++i)
		ans=max(ans,(a[i-2]+a[i-1])%a[i]);
	for (int j=n;j>=1;--j)
		if (a[j]-1>ans){
			int k=0;
			for (int i=1;i<j;++i) b[++k]=a[i];
			for (int i=j+1;i<=n;++i) b[++k]=a[i]%a[j];
			sort(b+1,b+k+1);
//			ans=max(ans,(b[k]+b[k-1])%a[j]);
			for (int i=k,p=1;i>=1;--i){
				p=min(p,i-1);
				while (p<=i-1 && b[p]+b[i]<a[j])
					++p;
				if (p>1)
					ans=max(ans,(b[p-1]+b[i])%a[j]);
			}
		}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-04-14 21:02  jz_597  阅读(272)  评论(0编辑  收藏  举报