【HDU5869】 Different GCD Subarray Query 题解 (线段树维护区间GCD)

题目大意:求区间$[L,R]$中所有子区间产生的最大公因数的个数。

-------------------------

对于$gcd$,我们知道$gcd(a,b,c)=gcd(gcd(a,b),c)$。所以我们可以利用$gcd$的传递性来求区间的$gcd$。如果$gcd$相同,那么保留下来位置相对靠右的那一个,这与我们查询的方式有关。我们在查询时是$O(n)$正向遍历的,询问的区间按照右端点进行关键字排序,每次维护一个新的$gcd$最靠右的位置并让这个位置+1,让之前的位置-1即可。

因为每次$gcd$结果至少除以2,所以复杂度降成了$\log$级别的。总复杂度$O(n\log n)$。

小细节:线段树建树的时候一定要从$0$开始建树,因为$pre[gcd]$是有可能等于$0$的。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=100005;
int a[maxn],n,T;
struct node
{
    int p,g;
};
struct qq
{
    int l,r,id;
}q[maxn];
struct t
{
    int l,r,sum;
}tree[maxn<<2];
vector<node> v[maxn];
int pre[10*maxn];
int res[maxn];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int gcd(int a,int b)
{
    if (!b) return a;
    return gcd(b,a%b); 
}
int cmp(qq a,qq b)
{
    return a.r<b.r;
}
inline void build(int index,int l,int r)
{
    tree[index].l=l;
    tree[index].r=r;
    if (l==r)
    {
        tree[index].sum=0;
        return;
    }
    int mid=(l+r)>>1;
    build(index*2,l,mid);
    build(index*2+1,mid+1,r);
}
void change(int index,int pos,int x)
{
    if (tree[index].l==tree[index].r)
    {
        tree[index].sum+=x;
        return;
    }
    int mid=(tree[index].l+tree[index].r)>>1;
    if (mid>=pos) change(index*2,pos,x);
    else change(index*2+1,pos,x);
    tree[index].sum=tree[index*2+1].sum+tree[index*2].sum;
}
int query(int index,int l,int r)
{
    if (l<=tree[index].l&&tree[index].r<=r)
    {
        return tree[index].sum;
    }
    int ans=0,mid=(tree[index].l+tree[index].r)>>1;
    if (mid>=l) ans+=query(index*2,l,r);
    if (mid<r) ans+=query(index*2+1,l,r);
    return ans;
} 
signed main()
{
    n=read(),T=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=T;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
    build(1,0,n);
    for (int i=1;i<=n;i++)
    {
        int x=a[i],y=i,k=v[i-1].size();
        for (int j=0;j<k;j++)
        {
            int u=v[i-1][j].g;
            int gg=gcd(u,x);
            if (x!=gg)
            {
                v[i].push_back((node){y,x});
                y=v[i-1][j].p;
                x=gg;
            }
        }
        v[i].push_back((node){y,x});
    }
    sort(q+1,q+T+1,cmp);
    int now=1;
    for (int i=1;i<=n;i++)
    {
        int k=v[i].size();
        for (int j=0;j<k;j++)
        {
            int pp=v[i][j].p;
            int gg=v[i][j].g;
            change(1,pre[gg],-1);
            pre[gg]=pp;
            change(1,pp,1);
        }
        while (q[now].r==i)
        {
            int id=q[now].id;
            res[id]=query(1,q[now].l,q[now].r);
            now++;
            if (now>T) break;
        }
    }
    //for (int i=1;i<=n<<2;i++) cout<<tree[i].sum<<endl;
    for (int i=1;i<=T;i++) printf("%lld\n",res[i]);
    return 0;
}

 

posted @ 2020-05-29 08:23  我亦如此向往  阅读(266)  评论(0编辑  收藏  举报