HDU 5869区间CGD不同种类数---树状数组+map统计区间不同种类数(离线)

本文介绍了一种解决区间子区间不同GCD种类数问题的算法。通过离线树状数组与map结合的方式,实现了对查询右端点排序并依次转移解决问题的方法。文章详细解析了解题思路,并给出了完整的代码实现。

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

http://acm.hdu.edu.cn/showproblem.php?pid=5869


题意:已知一个数组,Q个查询,问你区间子区间的不同GCD种类数


思路:先考虑一个数组区间不同个数,可以使用离线的树状数组实现,具体是对查询右端点进行排序,依次转移解决问题。

首先我们考虑固定右端点的查询区间不同数字,其实我们可以在每个数字出现的最右位置记录一下就可以了,统计起来就是sum[r]-sum[l]

这样子考虑右区间移动,就要改变那个数的最右位置,显然可以用一个map来维护每个数字出现的最右位置。

回到这个题目,显然一个点不只是一个数字,我们按照之前多校的一个处理方法,处理出来每个点作为右端点的子区间的GCD值(套路,而且一个点的不会很多,大致logAi个,用一个vector数组维护爽一下),这样子每个点就是一个数组了,但是统计的时候又不是区间所有点数组的不同数字个数,但是也很好魔改,其实我们只需要每个GCD出现的最右距离就可以了,因为如果查询的L在这个L左面,自然会被算进去,如果不在,那么就无视,以后的查询可能会需要,还是继续用map维护就好了。

所以这道题就解决啦!关键还是学会树状数组加map来统计区间不同种类数的方法。


代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
#define MAX 100005
const int INF=1e9+7;
int n,m;
int a[MAX];
int b[MAX];
int ans[MAX];
struct Q{
    int r,l,id;
}q[MAX];
vector<pair<int,int> > G[MAX];
map<int,int> H;

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

void modify(int x,int add){
    while(x<=MAX){
        b[x]+=add;
        x+=lowbit(x);
    }
}

int get_sum(int x){
    int ret=0;
    while(x!=0){
        ret+=b[x];
        x-=lowbit(x);
    }
    return ret;
}

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

bool cmp(const Q &q1,const Q &q2){
    if(q1.r!=q2.r)
    return q1.r<q2.r;
    else return q1.id<q2.id;
}

void F(int x){
    G[x].push_back(make_pair(a[x],x));
    int cnt=0;
    for(int i=0;i<G[x-1].size();i++){
        int gc=gcd(G[x-1][i].first,a[x]);
        if(gc!=G[x][cnt].first){
            G[x].push_back(make_pair(gc,G[x-1][i].second));
            cnt++;
        }
        else{
           // G[x][cnt].second=G[x-1][i].second;
        }
    }
}

void init(){
    for(int i=0;i<=n;i++) G[i].clear();
    H.clear();
    memset(b,0,sizeof(b));
    G[1].push_back(make_pair(a[1],1));
    for(int i=2;i<=n;i++) F(i);
}

int main(){
    int t;
    while (scanf("%d%d",&n,&m)!=EOF){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        init();
        int l,r;
        for(int i=0;i<m;i++){
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q,q+m,cmp);
        int R=1;
        for(int i=0;i<m;i++){
            while(R<=q[i].r){
                for(int j=0;j<G[R].size();j++){
                    if(H.count(G[R][j].first)!=0){
                        modify(H[G[R][j].first],-1);
                    }
                    H[G[R][j].first]=G[R][j].second;
                    modify(G[R][j].second,1);
                }
                R++;
            }
            ans[q[i].id]=get_sum(q[i].r)-get_sum(q[i].l-1);
        }
        for(int i=0;i<m;i++) printf("%d\n",ans[i]);

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值