Magical GCD UVA 1642 利用约数个数少来优化 给定n个数,求使连续的一段序列的所有数的最大公约数*数的数量的值最大。输出这个最大值。

/**
题目:Magical GCD UVA 1642
链接:https://vjudge.net/problem/UVA-1642
题意:给定n个数,求使连续的一段序列的所有数的最大公约数*数的数量的值最大。输出这个最大值。
思路:
从左到右枚举一段连续序列时候,同时不断取gcd,会发现gcd相同的部分。
gcd的值会随着长度边长非递增变化。最多logx个不同的gcd。那么对于一个长长的序列。
要是可以将相同gcd的一起处理,那么时间就可以优化。

枚举姿势要正确,保证当前枚举的位置,可以利用前面的结果。
常规枚举姿势,右边界位置从左到右枚举,然后枚举左边界位置j<=i从右往左枚举。
用d,l两个数组存储信息。
ll d[maxn];//d[i]表示到达i这个位置的最大公约数
ll l[maxn];//l[i]表示i这个位置到l[i]这个位置的最大公约数相同。
*/

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+100;
ll d[maxn];//d[i]表示到达i这个位置的最大公约数
ll l[maxn];//l[i]表示i这个位置到l[i]这个位置的最大公约数相同。
int T, n;
ll a[maxn];
ll GCD(ll a,ll b)
{
    return b==0?a:GCD(b,a%b);
}
int main()
{
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);

        ll ans = 0;
        for(int i = 1; i <= n; i++){
            d[i] = a[i];
            l[i] = i;
            ll gcd = a[i];
            ll pos = l[i];
            ll lastpos = pos;
            ans = max(ans,a[i]);
            pos--;
            while(pos>=1){
                pos = l[pos];
                ll gcdl = GCD(gcd,d[pos]);
                ans = max(ans,gcdl*(i-pos+1));
                if(gcdl==gcd){
                    l[lastpos] = pos;
                    d[pos] = gcdl;
                }else
                {
                    gcd = gcdl;
                    lastpos = pos;
                    d[pos] = gcdl;
                }
                pos--;
            }
        }
        printf("%lld
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/6775854.html