版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HuangXinyue1017/article/details/83473369
Time Limits: 2000 ms Memory Limits: 131072 KB
Description
对于一个由正整数组成的序列, Magical GCD 是指一个区间的长度乘以该区间内所有数字的最大公约数。给你一个序列,求出这个序列最大的 Magical GCD。
Input
单个测试点包含多组数据。
输入的第一行是一个整数T表示数据组数。
每组数据的第一行是一个整数N,描述序列长度。
接下来N个数字,描述这个序列元素A[i]。
Output
对于每组测试数据输出一行,包含一个整数,表示序列最大的 Magical GCD。
Sample Input
1
5
30 60 20 20 20
Sample Output
80
Data Constraint
对于 30%分值的数据,N <= 10,T <= 11,000, A[i] <= 100
对于剩余 70%分值的数据,N <= 100,000,T <= 20, A[i] <= 10^12
C/C++选手读入和输出 64 位整数请使用%lld。
Hint
【样例说明】
对于子区间 60 20 20 20,长度为 4,最大公约数为 20,此段 Magical GCD 为 80.
【评分标准】
对于每个测试点,你的输出必须和标准输出一致得到全部分数,否则不得分。
Solution
- 不难发现,固定了左端点后,右端点往右移GCD是单调不增的
- 考虑枚举右端点 , 表示区间 的GCD
- 我们发现对于 ,我们只需要保留 ,而不需要 ,为什么呢?
- 既然 ,那么当右端点继续右移时,他们的到右端点的GCD是一样的
- 而 的长度显然更优,那么 就没有存在的必要,我们用链表维护一下,跳过不必要的区间就好了
如果我的表述不清楚,可以看这个
Code
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=1e5+5;
int T,n,l[N],r[N];
long long ans,a[N],b[N];
long long gcd(long x,long y)
{
return y?gcd(y,x%y):x;
}
int main()
{
freopen("magical.in","r",stdin);
freopen("magical.out","w",stdout);
scanf("%d",&T);
while(T--)
{
scanf("%d",&n),ans=0;
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]),b[i]=a[i];
l[i]=i-1,r[i]=i+1;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=i;j=r[j])
{
b[j]=gcd(b[j],a[i]);
ans=max(ans,b[j]*(i-j+1));
if(b[j]==b[j-1]) r[l[j]]=r[j],l[r[j]]=l[j];
}
printf("%lld\n",ans);
}
}