「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;
}
posted @ 2020-02-02 23:14  Sangber  阅读(163)  评论(0编辑  收藏  举报