您的位置:首页 > 其它

【NOIP2014模拟8.17】Magical GCD

2016-10-12 20:08 489 查看

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,显然只有30分。

正解需要从暴力出发

在暴力时,右端点l向右移动一格会有什么改变呢?

设g[i]表示当前右端点为l,i到l的GCD

当l向右移时,g[i]单调不增

而且最多只有log2(n)个点,值也只有log2(a[i])种

那么用链表维护g[i],只要连续g[i]两个相同,就删掉右边的,只保留左边的,保证了单次时间复杂度为O(n∗log2(n))

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 101000
using namespace std;
int n,tot;
ll a
,g
;
struct node{
int l,r;
};
node e
;
ll gcd(ll m,ll n)
{
for(ll r=n;r!=0;r=m%n,m=n,n=r);
return m;
}
int main()
{
int ac;
for(scanf("%d",&ac);ac;ac--)
{
scanf("%d",&n);tot=0;
for(int i=1;i<=n;i++) e[i].r=i+1,e[i].l=i-1,scanf("%lld",&g[i]);
ll ans=0;
for(int l=1;l<=n;l++)
{
for(int i=1;i<=l;i=e[i].r)
{
g[i]=gcd(g[i],g[l]);ans=max(ans,g[i]*(ll)(l-i+1));
if(g[i]==g[e[i].l]) e[e[i].l].r=e[i].r,e[e[i].r].l=e[i].l;
}
}
printf("%lld\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: