「JSOI2015」最大公约数
「JSOI2015」最大公约数
传送门
考虑先枚举区间左端点,
然后我们会发现所有可能的区间虽然有 \(O(n)\) 个,但是本质不同的区间 \(\gcd\) 只有 \(\log n\) 级别,而且是从左端点往右呈阶梯状递减的。
所以说我们可以对于这 \(\log n\) 种不同的 \(\gcd\) 都算一遍答案。
具体来说就是二分出最远的那个可行右端点。
然后区间 \(\gcd\) 用 \(\text{ST}\) 表维护一下即可。
参考代码:
#include <algorithm>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
typedef long long LL;
const int _ = 1e5 + 5;
int n, lg[_]; LL st[22][_];
inline LL gcd(LL a, LL b) { return b != 0 ? gcd(b, a % b) : a; }
inline LL query(int l, int r) {
int x = lg[r - l + 1];
return gcd(st[x][l], st[x][r - (1 << x) + 1]);
}
int main() {
#ifndef ONLINE_JUDGE
file("cpp");
#endif
read(n);
for (rg int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
for (rg int i = 1; i <= n; ++i) read(st[0][i]);
for (rg int i = 1; i <= lg[n]; ++i)
for (rg int j = 1; j + (1 << i) - 1 <= n; ++j)
st[i][j] = gcd(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
LL ans = 0;
for (rg int i = 1; i <= n; ++i)
for (rg int p = i, nxt; p <= n; p = nxt + 1) {
int l = p, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (query(i, mid) == query(i, p)) nxt = mid, l = mid + 1;
else r = mid - 1;
}
ans = max(ans, 1ll * (nxt - i + 1) * query(i, nxt));
}
printf("%lld\n", ans);
return 0;
}