离线记录+树状数组(hdu 5869 统计任意区间的不同gcd值)

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

题意:给定一个数组,给出范围[l,r],求在这范围内的不同gcd值得个数(连续下标)

题解:用o(nlogA)计算出gcd值并进行记录,每次固定右值,枚举前一个得出的gcd值,记录左边的坐标和不同gcd值,然后用树状数组维护,具体见注释

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define MAX 100500
using namespace std;
int c[MAX];
int number[MAX];
int ans[MAX];
vector<pair<int,int> > value[MAX]; 每个点击记录从1--i的gcd值,first表示gcd值,second表示second--m的范围的左值
typedef struct
{
    int l,r;
    int position;
}Adress;
Adress ad[MAX];
int vis[1000050];
bool compare(Adress a,Adress b)
{
    return a.r<b.r;
}
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int v)
{
    for(int i=x;i<=MAX;i+=lowbit(i))
    {
        c[i]+=v;
    }
}
int gcd(int a,int b)
{
    if(!b) return a;
    else return gcd(b,a%b);
}
long long sum(int x)
{
    long long sum=0;
    while(x>0)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    int n,q;
    while(~scanf("%d %d",&n,&q))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&number[i]);
            value[i].clear();
        }
        for(int i=1;i<=n;i++)//遍历每个点
        {
            int num=number[i],id=i;
            for(int j=0;j<value[i-1].size();j++)//根据前一个点的gcd记录值来进行当前点的计算,即每次固定右值,遍历左值
            {
                int _gcd=gcd(max(num,value[i-1][j].first),min(num,value[i-1][j].first));
                if(_gcd != num)//防止重复
                {
                    value[i].push_back(make_pair(num,id));
                    num=_gcd;id=value[i-1][j].second;
                }
            }
            value[i].push_back(make_pair(num,id));
        }
        for(int i=1;i<=q;i++)
        {
            scanf("%d %d",&ad[i].l,&ad[i].r);
            ad[i].position=i;
        }
        sort(ad+1,ad+1+q,compare);
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
        int cnt=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<value[i].size();j++)//维护树状数组
            {
                int num=value[i][j].first;
                int id=value[i][j].second;
                if(vis[num]) add(vis[num],-1);
                vis[num]=id;
                add(id,1);
            }
            while(ad[cnt].r==i&&cnt<=q)//得出结论
            {
                ans[ad[cnt].position]=sum(ad[cnt].r)-sum(ad[cnt].l-1);cnt++;
            }
            if(cnt>q) break;
        }
        for(int i=1;i<=q;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值