【NOIP2014模拟8.17】Magical GCD

来源:互联网 发布:js实现可视化拖拽控件 编辑:程序博客网 时间:2024/05/24 02:20

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(nlog2(n))

Code

#include<cstdio>#include<cstring>#include<algorithm>#define ll long long#define N 101000using namespace std;int n,tot;ll a[N],g[N];struct node{    int l,r;};node e[N];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);    }}
1 0
原创粉丝点击