HDU 5869 Different GCD Subarray Query(计数区间不同GCD)

本文介绍了一种处理区间GCD查询的有效算法。通过预处理和离线处理询问,利用树状数组计数不同的GCD值,实现了快速响应大量询问。适用于竞赛编程中涉及GCD查询的问题。

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

题意:给出一个序列,m次询问,每次询问某个闭区间内所有的子区间组成的不同GCD有多少个

对于一个长度为n的序列来说,组成的不同GCD值不会超过n*logn个,所对应的区间也是如此,预处理出每个下标对应的所有不同gcd区间,右端点排序,对询问离线处理,右端点排序,通过树状数组计数不同的gcd值得到答案

#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 2000004

struct node
{
    int l,r;
    int g;
    node() {}
    node(int _l,int _r,int _g)
    {
        l=_l;
        r=_r;
        g=_g;
    }
    friend bool operator < (node A,node B)
    {
        if(A.r==B.r) return A.l<B.l;
        return A.r<B.r;
    }
} que[2*maxn];

int n,q;
int a[maxn];
int len;

int dp[maxn][30];
int mm[maxn];
void initRMQ(int n,int b[])
{
    mm[0] = -1;
    for(int i = 1; i <= n; i++)
    {
        mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
        dp[i][0] = b[i];
    }
    for(int j = 1; j <= mm[n]; j++)
        for(int i = 1; i + (1<<j) -1 <= n; i++)
            dp[i][j] = __gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmq(int x,int y)
{
    int k = mm[y-x+1];
    return __gcd(dp[x][k],dp[y-(1<<k)+1][k]);
}

void F(int k)
{
    int la=k;
    int now=a[k];
    que[len++]=node(k,k,now);
    while(now!=rmq(k,n))
    {
        int l=la+1,r=n;
        while(l+1<=r)
        {
            int mid=l+r>>1;
            if(rmq(k,mid)==now) l=mid+1;
            else r=mid-1;
        }
        for(int i=max(la,l-5); i<=min(n,l+5); i++)
        {
            if(rmq(k,i)!=now)
            {
                now=rmq(k,i);
                la=i;
                que[len++]=node(k,i,now);
                break;
            }
        }
    }
}

int c[maxn];

int lowbit(int x)
{
    return x&-x;
}

int query(int x)
{
    if(x==0) return 0;
    int sum=0;
    for(; x>0; x-=lowbit(x)) sum+=c[x];
    return sum;
}

void update(int x,int val)
{
    for(; x<=len; x+=lowbit(x)) c[x]+=val;
}

int ans[maxn];
struct quer
{
    int l,r;
    int id;
    friend bool operator <(quer a,quer b)
    {
        if(a.r==b.r) return a.l<b.l;
        return a.r<b.r;
    }
} qe[maxn];

map<int,int>mp;
int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        len=1;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        initRMQ(n,a);
        for(int i=1; i<=n; i++)
        {
            F(i);
        }
        sort(que+1,que+len);
        for(int i=0; i<q; i++)
        {
            scanf("%d%d",&qe[i].l,&qe[i].r);
            qe[i].id=i;
        }
        sort(qe,qe+q);
        int num=0;
        mp.clear();
        int r=1;
        memset(c,0,sizeof c);
        for(int i=0; i<q; i++)
        {
            while(r<len&&que[r].r<=qe[i].r)
            {
                if(que[r].l>mp[que[r].g])
                {
                    if(mp[que[r].g])
                    {
                        update(mp[que[r].g],-1);
                    }
                    mp[que[r].g]=que[r].l;
                    update(que[r].l,1);
                }
                r++;
            }
            ans[qe[i].id]=query(qe[i].r)-query(qe[i].l-1);
        }
        for(int i=0; i<q; i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}
/*
10 10
5 5 4 4 3 2 1 5 10 10
1 2
2 3
3 4
1 10
5 7
2 9
1 9
6 7
8 10
7 10
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值