• 【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(nlog 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
    ",res[i]);
        return 0;
    }
  • 相关阅读:
    用JS获取地址栏参数的方法(超级简单)
    返回前一页并刷新页面方法
    js 弹出确认 取消对话框
    微信“无法回答问题设置”失效
    百度地图api学习平台
    JQuery 定时器 (Jquery Timer 插件)
    select 框option添加属性 js计算价格 保持两位小数
    修改织梦分页标签样式
    一键导航
    关于memset赋值无穷大无穷小
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12985353.html
Copyright © 2020-2023  润新知