HDU5381 莫队 gcd 预处理

该博客介绍了如何利用莫队算法解决HDU5381问题,即求解给定区间内所有子区间的GCD和。作者提到在尝试用RMQ(区间最值查询)结合二分法后导致超时,后来通过预处理gcd降低了时间复杂度,预处理的时间复杂度为O(n*logn),这个方法基于gcd变化至少会除以2的性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:HDU 5381
题意:给出N个数,M个询问,对于每个询问,有一组[L,R],输出这个区间内所有子区间的GCD和。

思路:看完题目不难想到莫队,本来转移的时候想着可以rmq套个二分,然而超时了…
看了下别人题解才发现可以先预处理.

至于为什么可以这样预处理.我们发现 g c d gcd gcd 发生变化的话其值至少除2,因此种类顶多就 l o g n log n logn 种,预处理时间复杂度也就 O( n ∗ l o g n n*logn nlogn) ,还是可以接受的.

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

#define Fi first
#define Se second
#define ll long long
#define inf 0x3f3f3f3f
#define lowbit(x) (x&-x)
#define in(b) scanf("%d",&b)
#define mmin(a,b,c) min(a,min(b,c))
#define mmax(a,b,c) max(a,max(b,c))
#define debug(a) cout<<#a<<"="<<a<<endl;
#define debug2(a,b) cout<<#a<<"="<<a<<" "<<#b<<"="<<b<<endl;
#define debug3(a,b,c) cout<<#a<<"="<<a<<" "<<#b<<"="<<b<<" "<<#c<<"="<<c<<endl;
#define show_time cout << "The run time is:" << (double)clock() /CLOCKS_PER_SEC<< "s" << endl;
using namespace std;
const int N=3e5+10;
vector<pair<int,int> >vl[N],vr[N];
int a[N];
void init(int n)
{
    for(int i=0;i<=n+1;i++) vl[i].clear(),vr[i].clear();

    for(int i=1;i<=n;i++)
    {
        int gcd=a[i],p=i;
        for(auto e:vr[i-1])
        {
             int tmp=__gcd(gcd,e.Se);
             if(tmp!=gcd)
             {
                 vr[i].push_back({p,gcd});
             }
             p=e.Fi;gcd=tmp;
        }
        vr[i].push_back({p,gcd});
    }
    for(int i=n;i>=1;i--)
    {
        int gcd=a[i],p=i;
        for(auto e:vl[i+1])
        {
            int tmp=__gcd(gcd,e.Se);
            if(tmp!=gcd)
            {
                vl[i].push_back({p,gcd});
            }
            p=e.Fi;gcd=tmp;
        }
        vl[i].push_back({p,gcd});
    }
}
int len;
struct Node
{
    int l,r,id,block;
    Node(){}
    Node(int L,int R,int Id)
    {
        l=L;r=R;id=Id;
        block=l/len;
    }
}Q[N];
bool cmp(Node a,Node b)//奇偶优化
{
    if(a.block==b.block)
    {
        if(a.block&1)
        return a.r<b.r;
        else return a.r>b.r;
    }
    return a.l<b.l;
}
void add1(int l,int r,ll v,ll &ans)//左端点变化
{
    int bef=l;
    for(auto e :vl[l])
    {
        if(bef>r) break;
        ans+=v*(min(r,e.Fi)-bef+1)*e.Se;
        bef=e.Fi+1;
    }
}
void add2(int l,int r,ll v,ll &ans)//右端点变化
{
    int bef=r;
    for(auto e :vr[r])
    {
        if(bef<l) break;
        ans+=v*(bef-max(l,e.Fi)+1)*e.Se;
        bef=e.Fi-1;
    }
}

ll Ans[N];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n; scanf("%d",&n);
        len=sqrt(n);

        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int m; scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            int l,r;scanf("%d%d",&l,&r);
            Q[i]=Node(l,r,i);
        }
        init(n);

        sort(Q+1,Q+1+m,cmp);

        int l,r;l=r=1;
        ll ans=a[1];
        for(register int i=1;i<=m;i++)
        {
            int ql=Q[i].l,qr=Q[i].r;
            while(l<ql) add1(l++,r,-1ll,ans);
            while(l>ql) add1(--l,r,1ll,ans);
            while(r<qr) add2(l,++r,1ll,ans);
            while(r>qr) add2(l,r--,-1ll,ans);
            Ans[Q[i].id]=ans;
        }
        for(int i=1;i<=m;i++)
        {
            printf("%lld\n",Ans[i]);
        }
    }
    return 0;
}
/*

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值