[JSOI 2015]最大公约数

Description

题库链接(最近 $\text{bzoj}$ 维护上不去,就给洛谷的链接了...

给定长度为 $n$ 的正整数序列 $A$。定义一连续子段 $[l,r]$,其权值为 $$\left(\gcd\limits_{l\leq i\leq r}^{}A_i\right) \times (r-l+1)$$

求子段最大权值为多少。

$1\leq n\leq 100000,1\leq A_i\leq 10^{12}$

Solution

显然对于一个确定的右端点,其左端点所有取值中,一整段的 $\gcd$ 种数是不超过 $\log A_i$ 的。

我们只需存下这 $\log A_i$ 个公约数并记录对应的最靠左的左端点即可。

时间复杂度为 $O(n\log^2 A_i)$。

Code

#include <bits/stdc++.h>
#define ll long long
#define pli pair<ll, int>
#define fr first
#define sc second
#define pb push_back
using namespace std;
const int N = 100000+5;

int n, cnt;
ll a[N], ans;
vector<pli > g[N];

ll gcd(ll a, ll b) {return b ? gcd(b, a%b) : a; }
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i++) {
        cnt = 0;
        for (int j = 0, sz = g[i-1].size(); j < sz; j++) {
            pli x = g[i-1][j];
            ll t = gcd(a[i], x.fr);
            ans = max(ans, t*(i-x.sc+1));
            if (!cnt || t != g[i][cnt-1].fr) g[i].pb(pli(t, x.sc));
            ++cnt;
        }
        pli x = pli(a[i], i);
        ll t = gcd(a[i], x.fr);
        ans = max(ans, t*(i-x.sc+1));
        if (!cnt || t != g[i][cnt-1].fr) g[i].pb(pli(t, x.sc));
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2019-09-23 23:32  NaVi_Awson  阅读(266)  评论(4编辑  收藏  举报