2019年华南理工大学程序设计竞赛(春季赛) H Parco_Love_GCD(思维)

链接:https://ac.nowcoder.com/acm/contest/625/H
来源:牛客网
 

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

众所周知,在算法竞赛中,出题人对他出的题的难度往往存在错误的估计。比如出题人本想出个中等题,没想到却出成了简单题;本想出个自闭题,结果数据太水变成了签到题。因此,为了让一场比赛能有良好的体验,有一个靠谱的验题人是非常重要的。

CC出好题目后,便拿给小马哥看。不出所料,这些题目小马哥全都是看一眼就会做了。而且,小马哥觉得这些题对于参赛选手来说也太水了(5个签到题哦~)。为了避免发生全场十几个队AK这种紧急事态,小马哥决定把一道签到题改成简单题,于是便有了现在这个题目。

小马哥非常喜欢数论,尤其钟爱“最大公约数”(Greatest Common Divisor,简称GCD)这一概念,因此他打算出一道和GCD有关的题目。小马哥首先给出了含n个正整数的序列a1,a2,⋯,ana1,a2,⋯,an,然后让你考虑该序列的所有子区间的数对应的GCD值,也就是说考虑所有gcd(al,⋯,ar)gcd(al,⋯,ar)的值。显然,这样的值一共有n(n+1)2n(n+1)2个。一个中二出题人可能会让你求这n(n+1)2n(n+1)2数中第k大的数,但幸运的是,小马哥是个正经出题人,因此它只让你求这n(n+1)2n(n+1)2个数之和模109+7109+7的结果。
 

也就是要求下面这个式子:

ans=(∑nl=1∑nr=lgcd(al,⋯,ar))mod(109+7)ans=(∑l=1n∑r=lngcd(al,⋯,ar))mod(109+7)

小马哥在此预祝大家AK~

输入描述:

第一行是一个整数n (1≤n≤5×105)n (1≤n≤5×105),表示数的个数。

接下来一行给出n个整数,其中第i个整数aiai满足1≤ai≤1091≤ai≤109。

输出描述:

输出一行一个整数,表示ans的值。

示例1

输入

复制

5
16 4 7 21 3

输出

复制

72

题意:中问题意略

思路:第一反应是RMQ+二分。。。结果多了一个log然后就T了。。。

不难发现,当从a[r]开始,到a[r-1],a[r-2],...a[l]的gcd不变时(设为v[r]),我们就能一次统计答案了。由于gcd每一次减少至少/2,所以我们从每个r往前算的时候,最多有log(a[i])个不同的gcd。但是不能每次都一个一个往前找,我们用l[r]表示v[r]向前所能到达的最小下标,后面跳的时候l[i]就可以直接跳到l[l[i]-1]了。

代码:

#include<cstdio>
const int N=500010,P=1000000007;
int n,i,j,ans,a[N],l[N],v[N];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++)scanf("%d",&a[i]);
  for(i=1;i<=n;i++)for(v[i]=a[i],j=l[i]=i;j;j=l[j]-1){
    v[j]=gcd(v[j],a[i]);
    while(l[j]>1&&gcd(a[i],v[l[j]-1])==gcd(a[i],v[j]))l[j]=l[l[j]-1];
    ans=(1LL*v[j]*(j-l[j]+1)+ans)%P;
  }
  printf("%d",ans);
}
/*
5
16 4 7 21 3
72
*/

我在比赛的时候蠢蠢的用了HDU 5381 The sum of gcd的莫队做法(m=1,即只有一个1~n的查询),也能卡过。。。不过代码量和上面的解法差别可不是一点了。。。

代码:

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
#define N 500002
const ll mo=1e9+7;
struct node{
    int l;
    int r;
    int id;
};
struct sign{
    int g;
    int num;
    sign(int tempg=0,int tempnum=0){
        g=tempg;
        num=tempnum;
    }
};
int numa[N];
long long int ansa[N];
struct node querya[N];
int length;
vector<sign> rrgcd,rlgcd,llgcd,lrgcd;

bool cmp(struct node a,struct node b){
    if(a.l/length==b.l/length){
        return a.r<b.r;
    }
    else{
        return a.l/length<b.l/length;
    }
}

int gcd(int a,int b){
    int c;

    while(b){
        c=a%b;
        a=b;
        b=c;
    }

    return a;
}

int main(){
    int t;
    int n;
    int q;

    t=1;
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&numa[i]);
        }

        q=1;querya[1].l=1;querya[1].r=n;
        querya[1].id=1;
        for(length=0;length*length<=n;length++);
        sort(querya+1,querya+1+q,cmp);

        int kuainum=-1,RR=0;
        long long ansrr=0;
        for(int i=1;i<=q;i++){
            //printf("%d %d %d\n",i,querya[i].l,querya[i].r);
            if(querya[i].l/length!=kuainum){
                kuainum=querya[i].l/length;
                RR=(kuainum+1)*length-1;
                rrgcd.clear();
                rlgcd.clear();
                ansrr=0;
            }
            while(RR<querya[i].r){
                RR++;
                for(int j=0;j<rrgcd.size();j++){
                    rrgcd[j].g=gcd(rrgcd[j].g,numa[RR]);
                    ansrr+=(long long int)rrgcd[j].g*rrgcd[j].num%mo;
                    ansrr%=mo;
                }
                rrgcd.push_back(sign(numa[RR],1));
                ansrr+=(long long int)numa[RR];
                ansrr%=mo;
                int nowindex=0;
                for(int k=1;k<rrgcd.size();k++){
                    if(rrgcd[k].g==rrgcd[nowindex].g){
                        rrgcd[nowindex].num+=rrgcd[k].num;
                    }
                    else{
                        nowindex++;
                        rrgcd[nowindex]=rrgcd[k];
                    }
                }
                while(nowindex+1<rrgcd.size()){
                    rrgcd.pop_back();//因为gcd是单调减的,所以即使是按顺序加入,未排序,也是按照从大到小的顺序排的
                }

                if(rlgcd.size()==0){
                    //printf("%d %d\n",RR,numa[RR]);
                    rlgcd.push_back(sign(numa[RR],1));
                }
                else{
                    int tempid=rlgcd.size();
                    //printf("%d %d\n",tempid-1,rlgcd[tempid-1].g);
                    if(!(numa[RR]%rlgcd[tempid-1].g)){
                        rlgcd[tempid-1].num++;
                    }
                    else{//printf("wi shi da hao ren\n");
                        rlgcd.push_back(sign(gcd(rlgcd[tempid-1].g,numa[RR]),1));
                    }
                }
            }

            int LR=(kuainum+1)*length-1;
            LR=min(LR,querya[i].r);
            llgcd.clear();
            lrgcd.clear();
            long long int ansll=0;

            for(;LR>=querya[i].l;LR--){
                for(int j=0;j<llgcd.size();j++){
                    llgcd[j].g=gcd(llgcd[j].g,numa[LR]);
                    ansll+=(long long int)llgcd[j].g*llgcd[j].num%mo;
                    ansll%=mo;
                }
                llgcd.push_back(sign(numa[LR],1));
                ansll+=(long long int)numa[LR];
                ansll%=mo;
                int nowindex=0;
                for(int k=1;k<llgcd.size();k++){
                    if(llgcd[nowindex].g==llgcd[k].g){
                        llgcd[nowindex].num+=llgcd[k].num;
                    }
                    else{
                        nowindex++;
                        llgcd[nowindex]=llgcd[k];
                    }
                }
                while(nowindex+1<llgcd.size()){
                    llgcd.pop_back();
                }

                int tempid=lrgcd.size();
                if(tempid==0){
                    lrgcd.push_back(sign(numa[LR],1));
                }
                else{
                    int tempg=gcd(numa[LR],lrgcd[tempid-1].g);
                    if(tempg==lrgcd[tempid-1].g){
                        lrgcd[tempid-1].num++;
                    }
                    else{
                        lrgcd.push_back(sign(tempg,1));
                    }
                }
            }

            int tempid=querya[i].id;
            ansa[tempid]=(ansll+ansrr)%mo;
            for(int j=0;j<lrgcd.size();j++){
                for(int k=0;k<rlgcd.size();k++){
                    ansa[tempid]+=mo+(long long int)gcd(lrgcd[j].g,rlgcd[k].g)*lrgcd[j].num%mo*rlgcd[k].num%mo;
                    ansa[tempid]%=mo;
                }
            }
        }


        for(int i=1;i<=q;i++){
            printf("%lld\n",ansa[i]);
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/LSD20164388/article/details/89318014