CodeFouces Collatz Conjecture(GCD、数论)

题意:给定一个长度为n的数组,求其连所有续子串的GCD一共有几种。

思路:一开始想的是双重循环按照子串的开头的数字枚举子串并求其GCD,在去重。但是这样做先不计算GCD的时间复杂度,光模拟子串的过程就是(n+1)*n/2,而n最大为5*10^5,限制时间6秒,一定会超时。

后来转变思路,改用枚举以每个数字做结尾情况下的子串的GCD,这样,在计算下一个数字做结尾的子串的GCD的时候只需用到上个上个数字做结尾情况下的去重的GCD即可。这样需要对每种情况下的GCD做一定的处理:
例如:9 6 2 4这4个数字
以9结尾:9——>9    9结尾的情况下,GCD只有9一种情况。

以6结尾:9 6——>3
        6——>6    6结尾的情况下,GCD有3、6两种情况。
以2结尾:相当于在上个以6结尾的子串后面在添上一个6即可
        9 6 2——>1
        6 2——>2
        2  ——>2    2结尾的情况下GCD有1、2两种情况。

以4结尾:9 6 2 4——>1(相当于GCD(9,6,2)=1在于4取GCD)
        6 2 4 == GCD(6,2)=2 4——>2
        4——>4    4结尾的情况下GCD有1、2、4
最后将每个数字做结尾的子串得到的GCD放到数组ans中用unique()去重。(去重前要ans排序)

需要开辟一个存放每个数字最结尾的子串得到的GCD的数组temp,ai最大10^18,数组开到18lg10就够用。在循环中的长度相当于一个常数,外层循环取结尾数字时的循环长度为n,算上GCD时间复杂度为lg2,这样总体的时间复杂度n*18lg10*lg2,相当于常数*n==n。就不会超时啦~

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
LL GCD (LL a,LL b)
{
    return b==0?a:GCD(b,a%b);
}
LL num[500005],ans[500005];;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%I64d",&num[i]);
    LL temp[n+1];
    int cnt=1,sum=0,ends=0;
    LL t;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<ends;j++){
            t=GCD(num[i],temp[j]);
            if(temp[j]!=t)
            {
                ans[sum++]=temp[j];
                temp[j]=t;
                ///cnt++;
            }
        }
        temp[ends++]=num[i];///将上面for循环得到的GCD情况进行记录,含重复
        sort(temp,temp+ends);
        ends=unique(temp,temp+ends)-temp;///对每种数字结尾的子串的GCD情况进行去重
        ///ends=cnt;
        ///cnt=1;
    }
    for(int i=0;i<ends;i++)///不要忘记退出循环后,以a[n-1]结尾的GCD还要进行统计。
        ans[sum++]=temp[i];
    sort(ans,ans+sum);
    int x=unique(ans,ans+sum)-ans;
    printf("%d\n",x);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a17865569022/article/details/80170872
今日推荐