#双指针#洛谷 7521 [省选联考 2021 B 卷] 取模

题目传送门


分析

\(a\) 排序后从大到小枚举 \(a_k\),注意枚举的时候重复的只考虑一次,那么可以将其它数按照模 \(a_k\) 后排序,

答案只可能来自最大值与次大值之和取模或者之和最接近 \(a_k\)(后者利用双指针)

如果答案已经不小于 \(a_k\) 就退出整个循环,这样复杂度就是 \(O(n\log n\log a_i)\)

浅浅地证明一下,如果最终答案 \(ans\) 介于 \(a_{i-1}\)\(a_i\) 之间。

那么对于任意 \(j\geq i\)\(ans\geq (a_j+a_{j+1})\bmod {a_{j+2}}\),因为一定能枚举到 \(a_{j+2}\) 计算答案。

可以发现取模是一定能模掉的,否则不可能比 \(a_i\) 小,所以 \(ans+a_{j+2}\geq a_j+a_{j+1}\)

移项得到 \(a_{j+2}-ans\geq (a_j-ans)+(a_{j+1}-ans)\),那么 \(\{a_i-ans\}\) 其实增长速度比斐波那契数列还要快,

\(10^8\) 内其实只有 \(\log\) 级别,复杂度大概就是这么证明的。


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int N=200011;
int n,m,a[N],b[N],c[N],ans;
int iut(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int main(){
    n=iut();
    for (int i=1;i<=n;++i) b[i]=a[i]=iut();
    sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
    for (int i=m;i;--i){
        if (b[i]<=ans) break;
        for (int j=1;j<=n;++j) c[j]=a[j]%b[i];
        sort(c+1,c+1+n);
        if (c[n]+c[n-1]>=b[i]) ans=max(ans,c[n]+c[n-1]-b[i]);
            else ans=max(ans,c[n]+c[n-1]);
        for (int j=2,k=n;j<=n;++j){
            while (k>1&&(j==k||c[j]+c[k]>=b[i])) --k;
            if (k>1) ans=max(ans,c[j]+c[k]);
        }
    }
    return !printf("%d",ans);
}
posted @ 2024-02-12 00:46  lemondinosaur  阅读(26)  评论(0编辑  收藏  举报