Description
题库链接(最近 ( ext{bzoj}) 维护上不去,就给洛谷的链接了...
给定长度为 (n) 的正整数序列 (A)。定义一连续子段 ([l,r]),其权值为 [left(gcdlimits_{lleq ileq r}^{}A_i ight) imes (r-l+1)]
求子段最大权值为多少。
(1leq nleq 100000,1leq A_ileq 10^{12})
Solution
显然对于一个确定的右端点,其左端点所有取值中,一整段的 (gcd) 种数是不超过 (log A_i) 的。
我们只需存下这 (log A_i) 个公约数并记录对应的最靠左的左端点即可。
时间复杂度为 (O(nlog^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
", ans);
return 0;
}