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

来源:互联网 发布:淘宝店页头尺寸 编辑:程序博客网 时间:2024/05/30 19:34

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] );    }}
阅读全文
0 0
原创粉丝点击