[BZOJ4052][Cerc2013]Magical GCD
[BZOJ4052][Cerc2013]Magical GCD
试题描述
给出一个长度在 100 000 以内的正整数序列,大小不超过 10^12。
求一个连续子序列,使得在所有的连续子序列中,它们的GCD值乘以它们的长度最大。
输入
本题为多组数据。
第一行一个整数 T,表示数据组数。
每组数据第一行为一个整数 n,表示序列长度;第二行为 n 个整数表示序列。
输出
对于每组数据,输出 max{ gcd * length }
输入示例
1 5 30 60 20 20 20
输出示例
80
数据规模及约定
见“试题描述”
题解
可以发现,随着子序列的增长,gcd 要么不变,若变化则变化后的 gcd 不会超过原来的 1/2,所以 gcd 的变化次数不会超过 log21012 = 12 · log210 次。
有了上面的结论,我们就可以枚举左端点,然后向右二分 gcd 即将变化的位置(维护区间 gcd 可以用 ST 表),在每个这样的位置更新答案,然后移动子序列右端点,重复这个过程。设数字大小为 A,那么更新答案次数为 log2A;每次二分需要 log2n;哦对了虽然 RMQ 询问复杂度 O(1),但是每次需要取 gcd,最坏情况 log2A;别忘了我们还枚举了左端点有一个 n,所以最终复杂度不会超过 O(n · log22A · log2n)。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; #define LL long long const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } LL read() { LL x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 100010 #define maxlog 17 int n; LL A[maxn]; LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } LL Gcd[maxlog][maxn]; int Log[maxn]; void rmq_init() { Log[1] = 0; for(int i = 2; i <= n; i++) Log[i] = Log[i>>1] + 1; for(int i = 1; i <= n; i++) Gcd[0][i] = A[i]; for(int j = 1; (1 << j) <= n; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) Gcd[j][i] = gcd(Gcd[j-1][i], Gcd[j-1][i+(1<<j-1)]); return ; } LL query(int l, int r) { int len = r - l + 1, t = Log[len]; return gcd(Gcd[t][l], Gcd[t][r-(1<<t)+1]); } int main() { int T = read(); while(T--) { n = read(); for(int i = 1; i <= n; i++) A[i] = read(); rmq_init(); LL ans = 0; for(int L = 1; L <= n; L++) { int R = L; LL tmp = query(L, R); while(R < n) { int l = R, r = n + 1; while(r - l > 1) { int mid = l + r >> 1; if(query(L, mid) == tmp) l = mid; else r = mid; } R = l; ans = max(ans, tmp * (R - L + 1)); R++; tmp = query(L, R); } if(R <= n) ans = max(ans, tmp * (R - L + 1)); // mark!! } printf("%lld\n", ans); } return 0; }
大视野数据好强!我就因为少加 "mark!!" 那一行导致 WA 了一上午。。。