【NOIP2014模拟8.17】Magical GCD//2018.2.5

题目

Description
对于一个由正整数组成的序列, Magical GCD 是指一个区间的长度乘以该区间内所有数字的最大公约数。给你一个序列,求出这个序列最大的 Magical GCD。
Input

单个测试点包含多组数据。
输入的第一行是一个整数T表示数据组数。
每组数据的第一行是一个整数N,描述序列长度。
接下来N个数字,描述这个序列元素A[i]。

Output

对于每组测试数据输出一行,包含一个整数,表示序列最大的 Magical GCD。

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。


解题思路

这道题看起来像RMQ【其实正解也是RMQ(st算法/区间动态规划)】,但是数据不大。直接用暴力枚举【“记忆化枚举”】,当然要优化(否则O(n的平方)吃不消))。从左边列举到右边,用ans记录最大值,每次更新……


代码

#include
#include
#include
using namespace std; 
long long t,n,ans,a[100001],b[100001];
int d[100001],c[100001];
long long gf(long long x,long long y)
{return x%y?gf(y,x%y):y;}//辗转相除法
int main()
{
    scanf("%lld",&t);
    while (t--)
    {
        ans=0;
        scanf("%lld",&n);
        for (int i=1;i<=n;i++)
        {scanf("%lld",&a[i]); b[i]=a[i]; c[i]=i-1; d[i]=i+1;} //c记录左边的起始位置,d记录右边的终止位置
        for (int i=1;i<=n;i++)
         for (int j=1;j<=i;j=d[j])
         {
            b[j]=gf(b[j],a[i]);//求最大公约数
            ans=max(b[j]*(i-j+1),ans);//区间的Magical GCD数
            if(b[j]==b[j-1])//假如两个区间的Magical GCD数相同,直接把左、右边界扩大,这样就相当于“记忆化枚举”
            {
                d[c[j]]=d[j];
                c[d[j]]=c[j];
            }
         }
         printf("%lld\n",ans);
    }
}

你可能感兴趣的:(暴力(/模拟/字符串处理),倍增(RMQ/st算法,树上倍增))