2019 华工春季赛 H-Parco_Love_GCD (CDQ分治)

题目链接:哆啦A梦传送门

题意:计算   

 

 

题解:参考官方标程,我这只是解释一下。

分治。

每次在分治区间 [l,r] 中计算 端点x在 [l,mid]端点y在 [mid+1,r] 连续区间 [x,y] 的最大公约数。

那么我们只需从mid往前一直到 左边界l 求出 它们的gcd,并标记。

mid+1往后一直到右边界 r 求出它们的gcd ,并标记。

然后我们枚举 区间 [l,mid] 的不同gcd1数与 区间[mid+1,r] 的不同gcd2数 ,求它们两者的gcd=gcd(gcd1,gcd2) ,再乘以它们的数量,即为解。

 

 

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e5+10;

typedef long long LL;

const LL mod=1e9+7;

map<int,int> mp1,mp2;
int a[maxn];

int gcd(int a,int b)
{
    if(!b) return a;
    else return gcd(b,a%b);
}

LL ans=0;

void CDQ(int l,int r)
{
    int mid=(l+r)>>1;
    if(l==r){
        ans=ans+a[l];
        if(ans>=mod) ans-=mod;
        return;
    }

    mp1.clear();
    mp2.clear();

    int item=0;
    ///从mid往前gcd,并用map标记
    for(int i=mid;i>=l;i--)
    {
        item=gcd(item,a[i]);
        mp1[item]++;
    }

    item=0;
    ///从mid+1往后gcd
    for(int i=mid+1;i<=r;i++)
    {
        item=gcd(item,a[i]);
        mp2[item]++;
    }
    ///我们一定要从mid两边扩去求gcd并标记,因为这样才能使得求得的连续的区间gcd

    ///枚举两个半区间的gcd
    ///例如有五个数 16 16 16 16 2 ,假设现在 l=1,r=5,那么mp1[16]=3 ,mp2[16]=1,mp2[2]=1
    ///gcd(16,16)=16 ,3*1个连续的区间gcd=16 (区间分别为 [1,4],[2,4],[3,4]),显然其它也同理
    for(auto p : mp1)
        for(auto q : mp2)
    {
        ///表示有p.second*q.second个连续区间gcd=gcd(p.first,q.first)的子区间
        ans+=(gcd(p.first,q.first)*p.second%mod*q.second%mod);
        if(ans>=mod) ans-=mod;

    }

    ///继续分治
    CDQ(l,mid);
    CDQ(mid+1,r);
}
int main()
{

    int n;
    scanf("%d",&n);

    for(int i=1;i<=n;i++)
        scanf("%d",a+i);

    CDQ(1,n);

    printf("%lld\n",ans);






    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值