(Description)
给定(n)个数的序列(a_i)。求所有连续子序列中,序列长度 × 该序列中所有数的gcd 的最大值。
(nleq10^5, a_ileq10^{12})。
(Solution)
gcd有结合律,而且gcd每次改变至少会变小两倍,而且只会减小。
所以对于每个右端点,可以暴力维护每种gcd出现的最靠前的位置(只有(log)种gcd)。
详细一点就是这样的:
枚举右端点(i)。
栈里现在维护的是右端点为(i-1)时,每种(gcd_j),以及该(gcd_j)的最靠前的位置(p_j)(当右端点为(i-1),左端点在([p_j, p_{j+1}))的时候,(gcd(a_l,a_{l+1},...,a_{i-1}))的值为(gcd_j))(只需要用到最远的距离,所以就只存最靠前的位置了)。
这样栈里一直最多只有(log)个元素。
然后用(a_i)更新栈里的元素,就是所有元素都和(a_i)求一下(gcd)。这样我们能得到一个新的栈。
然后对这个栈去重,就是相邻(j,j+1)的(gcd)可能相同,把它们合并((j+1)扔掉),只保留位置更靠前的那个(j)就好了。
在得到这个新栈的时候,每次和(gcd_j)求一次(gcd)(假设结果是(g)),都可以用(g*(i-p_j+1))更新答案。
//1116KB 620MS
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=105;
char IN[MAXIN],*SS=IN,*TT=IN;
inline LL read()
{
LL now=0; register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
LL Gcd(LL x,LL y)
{
return y?Gcd(y,x%y):x;
}
int main()
{
static int pos[N];
static LL val[N];
for(int T=read(); T--; )
{
int n=read(); LL ans=0;
for(int i=1,t=0; i<=n; ++i)
{
LL ai=read();
int las=t; t=0;
for(int j=1; j<=las; ++j)
{
LL tmp=Gcd(val[j],ai);
if(val[t]!=tmp) val[++t]=tmp, pos[t]=pos[j], ans=std::max(ans,tmp*(i-pos[j]+1));
}
if(val[t]!=ai) val[++t]=ai, pos[t]=i, ans=std::max(ans,ai);
}
printf("%lld
",ans);
}
return 0;
}