Magical GCD

3 篇文章 0 订阅

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 i+2j这个区间中最大的Magical GCD,方程:
f[i][j]=GCD(f[i][j1],f[i+2j1][j1])
时间复杂度 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);
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值