hdu 5869 求区间不同gcd数 离线+树状数组+rmq二分

18 篇文章 0 订阅
9 篇文章 0 订阅

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 aa of NN positive integers a1,a2,⋯aN−1,aNa1,a2,⋯aN−1,aN; a subarray of aa is defined as a continuous interval between a1a1 and aNaN. In other words, ai,ai+1,⋯,aj−1,ajai,ai+1,⋯,aj−1,aj is a subarray of aa, for 1≤i≤j≤N1≤i≤j≤N. For a query in the form (L,R)(L,R), tell the number of different GCDs contributed by all subarrays of the interval [L,R][L,R].

Input
There are several tests, process till the end of input.

For each test, the first line consists of two integers NN and QQ, denoting the length of the array and the number of queries, respectively. NN positive integers are listed in the second line, followed by QQ lines each containing two integers L,RL,R for a query.

You can assume that

1≤N,Q≤1000001≤N,Q≤100000 

1≤ai≤10000001≤ai≤1000000
Output
For each query, output the answer in one line.
Sample Input
5 3
1 3 4 6 9
3 5
2 5
1 5
Sample Output
6
6
6

给n个数,m个询问,问区间l,r 的gcd种类总数

求区间不同数,在线的用主席树,离线的用树状数组,显然离线+树状数组比较简单。
先把查询排个序
就是每求到一个数 把前面所有的的gcd 往右移,那么gcd就在最接近右边界的地方,那么对于q[i].r==i 的询问,直接用树状数组进行统计即可,如果 有 多个数,只保留最右的一个所在位置,是思想核心 不断的消去和右移

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x)&(-x)
const int N = 102000;

int f[N][20];
int nu[N],tr[N],n,m,ans[N];
map<int,int> mp;


void add(int x,int val)
{
    while(x<N-50)
    {
        tr[x]+=val;
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int res=0;
    while(x>0)
    {
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}


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

struct node
{
    int l,r,id;
}q[N];

int cmp(node a,node b)
{
    if(a.r==b.r) return a.l<b.l;
    return a.r<b.r;
}

void init()
{
    for(int i=1;i<=n;i++)
        f[i][0]=nu[i];
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}


int rmq(int i,int j)
{
    int k=0;
    while(1<<(k+1)<=(j-i+1)) k++;
    return gcd(f[i][k],f[j-(1<<k)+1][k]);
}


int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        mp.clear();
        memset(tr,0,sizeof(tr));
        for(int i=1;i<=n;i++)
            scanf("%d",&nu[i]);
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }

        sort(q+1,q+m+1,cmp);
        int tot=1;

        for(int i=1;i<=n;i++)
        {
            int l=1,r=i;
            int g=nu[i];
            int j=i;
            while(r>=1)
            {
                l=1;
                while(l<=r)
                {
                    int mid=(l+r)>>1;
                    if(rmq(mid,i)==g)
                    {
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                if(!mp[g])
                {
                    add(j,1);
                }
                else if(mp[g]<j&&mp[g])
                {
                    add(mp[g],-1);
                    add(j,1);
                }
                mp[g]=j;
                g=rmq(r,i);
                j=r;
            }
                while(tot<=m&&q[tot].r==i)
                {
                    ans[q[tot].id]=sum(q[tot].r)-sum(q[tot].l-1);
                    tot++;
                }
        }
        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i] );
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值