[hdu5869]离线思想统计区间元素数量

题目描述

This is a simple problem. The teacher gives Bob a list of problems about GCD (Greatest Common Divisor). After studying some of them, Bob thinks that GCD is so interesting. One day, he comes up with a new problem about GCD. Easy as it looks, Bob cannot figure it out himself. Now he turns to you for help, and here is the problem:

Given an array a of N positive integers a1,a2,⋯aN−1,aN; a subarray of a is defined as a continuous interval between a1 and aN. In other words, ai,ai+1,⋯,aj−1,aj is a subarray of a, for 1≤i≤j≤N. For a query in the form (L,R), tell the number of different GCDs contributed by all subarrays of the interval [L,R].

算法思路

  1. 一般查询模式有两种模式,我想他们是和CS模式一一对应的,即离线算法和在线算法。在线算法指的是我们来一个查询就返回一个查询。很显然,一般应用在我们的知识数据库不再需要大量改动的时候。而对于离线算法,我们将所有的查询储存下来,然后在查询的过程中我们可能还要更新查询的数据库,来获得后面的解。
  2. 这一题就是典型的离线算法,我们固定按顺序求出每一个右端点和其之前的元素可能构造出来的gcd,并且储存其左端点,然后将每一个查询按照右端点从小到大的顺序储存下来,那么,在查询的过程中,对于一个gcd,如果前面的子数组也产生了同样的gcd,那么就更新,对一个右端点更新完成之后在查询。
  3. 查询的数据结构多种多样,树状数组,线段树,随便选择都好

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<utility>
using namespace std;

#define MAXN 100005

int Arr[MAXN];
int N,Q;
int pos[MAXN*10];
vector<pair<int,int> >queryS[MAXN];
vector<pair<int,int> >gcdS[MAXN];
int ans[MAXN];
int treeArr[MAXN];

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

void Init()
{
    int i,tmp,ts,j;

    for(i=1;i<=N;i++){
        tmp = Arr[i];
        ts = i;
        for(j=0;j<gcdS[i-1].size();j++){
            int tmpgcd = gcd(tmp,gcdS[i-1][j].first);
            if(tmpgcd != tmp){
                gcdS[i].push_back(make_pair(tmp,ts));
                ts = gcdS[i-1][j].second;
                tmp = tmpgcd;
            }
        }
        gcdS[i].push_back(make_pair(tmp,ts));
    }

    return;
}

void Add(int cur,int num)
{
    int k = cur;
    while(k<=N){
        treeArr[k] += num;
        k += k&(-k);
    }

    return;
}

int Query(int cur)
{
    int k = cur,sum = 0;
    while(k){
        sum += treeArr[k];
        k -= k&(-k);
    }
    return sum;
}

void Solve()
{
    memset(pos,0,sizeof(pos));
    memset(treeArr,0,sizeof(treeArr));
    int i,j;

    for(i=1;i<=N;i++){
        for(j=0;j<gcdS[i].size();j++){
            if(pos[gcdS[i][j].first]){
                //update 
                Add(pos[gcdS[i][j].first],-1);
            }
            Add(gcdS[i][j].second,1);
            pos[gcdS[i][j].first] = gcdS[i][j].second;
        }
        for(j=0;j<queryS[i].size();j++){
            ans[queryS[i][j].second] = Query(i)-Query(queryS[i][j].first-1);
        }
    }

    for(i=1;i<=Q;i++)
        printf("%d\n",ans[i]);

    return;
}

int main()
{
    freopen("input","r",stdin);
    int i,tmp1,tmp2;

    while(scanf("%d%d",&N,&Q)!=EOF){
        for(i=1;i<=N;i++){
            scanf("%d",&Arr[i]);
            queryS[i].clear();
            gcdS[i].clear();
        }

        Init();
        for(i=1;i<=Q;i++){
            //using vector to sort the query
            //by the end index
            //
            scanf("%d%d",&tmp1,&tmp2);
            queryS[tmp2].push_back(make_pair(tmp1,i));
        }

        Solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值