UVA 1642 MagicalGCD 题解

题面

本题是一道区间最大公约数的模板题;

如果N^2暴力的话当然会超时,所以我们要发掘出区间gcd的特点;

设gcd[i]表示区间[1,i]的最大公约数;

我们可以发现,从一个点i到1之间的所有区间的gcd均满足gcd[j]=GCD(gcd[j-1],a[j]);

由于gcd的性质,所以gcd[]是单调不增的;

接着,我们似乎发现了一个更神奇的事情,g[]中不同的值最多有log(max(a[i]))个;

换句话说,虽然g[]数组的长度为n,但在以上两个条件的限制下,最多仅仅有logA个值发生改变的地方;

所以我们遍历一遍右端点,处理出每个i到j(0<=i<=j)的区间的最大公约数;

注意,这并不再是n^2了,因为从i到j在long long 范围内最多有64个不同的值,所以我们只要遍历一遍这些点i就可以知道所有的0<=i<=j到j的区间的gcd;

神奇吧?

综上所述,求出所有区间的gcd仅仅需要nlogA的时间,完全可以切掉这道题;

#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define ll long long
using namespace std;
int n;
long long a[100010];
long long gcd(long long a,long long b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
vector<pair<long long ,long long> > vec[100010]; //第一维是值,第二维是在枚举右端点所用的点的编号
int main ()
{
    cin>>n;
    for(register int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(register int i=1;i<=n;i++){
        long long x=a[i],y=i;        
        vec[i].push_back(make_pair(x,y));        
        for(register int j=0;j<vec[i-1].size();j++){
            if(x==1) break;
            long long GCD=gcd(x,vec[i-1][j].first);    
            if(GCD!=x){
                x=GCD;
                y=vec[i-1][j].second;
                vec[i].push_back(make_pair(x,y));
            }
        }
    }
    long long ans=0;
    for(register int i=1;i<=n;i++){
        int w=vec[i].size()-1;
        for(register int j=0;j<w;j++){
            pair<long long,long long> p1=vec[i][j];
            pair<long long,long long> p2=vec[i][j+1];
            ans=max(ans,p1.first*(i-p2.second)); //注意,i-p2.second却不加1的原因是因为该区间其实是[p2.second,i-1];
        }
        ans=max(ans,vec[i][vec[i].size()-1].first*i);
    }
    cout<<ans;
}
原文地址:https://www.cnblogs.com/kamimxr/p/11295135.html