Description:
对于一个由正整数组成的序列, Magical GCD 是指一个区间的长度乘以该区间内所有数字的最大公约数。给你一个序列,求出这个序列最大的 Magical GCD。
INPUT:
单个测试点包含多组数据。
输入的第一行是一个整数T表示数据组数。
每组数据的第一行是一个整数N,描述序列长度。
接下来N个数字,描述这个序列元素A[i]。
1
5
30 60 20 20 20
OUTPUT:
对于每组测试数据输出一行,包含一个整数,表示序列最大的 Magical GCD。
80
分析:
题目是要求区间极值,很容易就想到ST算法。
设f[i][j]表示
i
到
f[i][j]=GCD(f[i][j−1],f[i+2j−1][j−1])
时间复杂度
o(nlog(n))
,哈哈,这题切了。
等会,貌似假如最大的区间长度不是
2x
呢?
貌似会炸……
怎么办???????
我们知道:Magical GCD(l,r)=Magical GCD(Magical GCD(l,x),Magical GCD(y,r))。如图:
那么我们就可以枚举r,然后二分一个点l,使得Magical GCD(l,r)=a[r], 更新答案,将r移到l,直到r=0为止。
代码不优美的话加一个读入优化就过了(仅限c++),然而我并没有加。
CODE:
#include<cstdio>
#include<cstring>
#include<algorithm>
long long t,n,a[100001],f[100001][17],log[300001];
long long read()
{
long long l=0;
char c;
c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') {l=l*10+c-'0';c=getchar();}
return l;
}
long long gcd(long long a,long long b)
{
long long c;
while (b>0)
{
c=a%b;
a=b;
b=c;
}
return a;
}
long long max(long long a,long long b)
{
if (a>b) return(a); else return(b);
}
void work()
{
//n=read();
scanf("%lld",&n);
long long i,j,k;
for (i=1;i<=n;i++)
{
//a[i]=read();
scanf("%lld",&a[i]);
f[i][0]=a[i];
}
long long ans=0;
for(i=1;i<=log[n];i++)
{
for(j=1;j<=n-(1<<i)+1;j++)
{
f[j][i]=gcd(f[j][i-1],f[j+(1<<i-1)][i-1]);
}
}
for(i=1;i<=n;i++)
{
if(ans<f[i][0]) ans=f[i][0];
long long x=f[i-1][1];
int l=1,r=i;
while(r!=0)
{
l=1;
if(x*i<=ans)break;
while(l<=r)
{
int mid=(l+r)/2;
int len=i-mid+1;
if(gcd(f[mid][log[len]],f[i-(1<<log[len])+1][log[len]])!=x)
l=mid+1;
else
r=mid-1;
}
ans=max(ans,(i-r)*x);
if(r>0) x=gcd(x,f[r][0]);
}
}
printf("%lld\n",ans);
}
int main()
{
long long i=1,j,k=2;
int y=1;
while (k<=300000)
{
for (j=y;j<=k-1;j++) log[j]=i-1;
y=k;
k*=2;
i++;
}
freopen("1.in","r",stdin);
scanf("%lld",&t);
for (i=1;i<=t;i++)
{
work();
}
fclose(stdin);
}