题意:
有一个n个数的序列, 定义magic gcd为一个连续子序列的gcd乘上子序列长度, 求magic gcd的最大值。
思路:
对于以i为右端点的子序列,左端点从i到1,gcd肯定是越来越小的, 并且gcd是分段的,也就是说,对于一段连续的左端点[a,b], gcd(a,a+1,... i) = gcd(b, b + 1,... i), 由于gcd减小一次最少除以2,所以对于每个右端点,gcd最多有log10^12段, 而对于每段,取左端点和gcd就行了。而对于每个右端点的分段可以递推算出。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <stack>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <ctime>
#include <cstdlib>
using namespace std;
#define mxn 100020
#define mxe 200020
#define mod 10000007
#define LL long long
#define inf 0x3f3f3f3f
#define vi vector<int>
#define PB push_back
#define MP make_pair
#define pii pair<int, int>
#define G(i, u) for(int i = fst[u]; ~i; i = nxt[i])
#define F(i, n) for(int i = 1; i <= n; ++i)
#pragma comment(linker,"/STACK:1024000000,1024000000")
#define ls (i << 1)
#define rs (ls | 1)
#define md ((ll + rr) >> 1)
int n;
LL a[mxn];
vector<pair<int, LL> > g[mxn];
void init() {
for(int i = 0; i < mxn; ++i)
g[i].clear();
}
LL gcd(LL x, LL y) {
if(y == 0) return x;
return gcd(y, x % y);
}
int main() {
// freopen("tt.txt", "r", stdin);
int cas;
scanf("%d", &cas);
while(cas--) {
init();
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
g[1].PB(MP(0, a[1]));
for(int i = 2; i <= n; ++i) {
LL G = gcd(a[i], a[i-1]);
if(G != a[i])
g[i].PB(MP(i - 1, a[i]));
int len = g[i-1].size();
for(int j = 0; j < len; ++j) {
int p = g[i-1][j].first;
LL val = g[i-1][j].second;
if(p == 0) {
g[i].PB(MP(0, G));
continue;
}
LL tmp = gcd(G, a[p]);
if(G == tmp) continue;
g[i].PB(MP(p, G));
G = tmp;
}
}
LL ans = n;
for(int i = 1; i <= n; ++i) {
int len = g[i].size();
for(int j = 0; j < len; ++j) {
int p = g[i][j].first;
LL val = g[i][j].second;
ans = max(ans, (i - p) * val);
}
}
printf("%lld\n", ans);
}
return 0;
}