hdu 5381 The sum of gcd

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=5381

题意

给你\(n\)个数,\(q\)次查询,每次询问\(l,r\),输出\(f(l,r)=\sum_{i=l}^r\sum_{j=i}^rgcd(a_i,a_{i+1},...,a_r)\)

思路

首先对于任意一个\(a[i]\),每次\(gcd\)减小至少一半,所以它向后的\(gcd\)最多下降\(log(a[i])\)次,可以求出对于每一个\(a[i]\)来说的\(gcd\)相同的各个区间。
用线段树维护一段区间的\(gcd\),可以查询一段\([l,r]\)\(gcd\)的值\(x\),从\(i\)开始枚举左边界\(l\),然后用二分查找到\(gcd\)相同的区间的右边界\(r\),这个就得到了对于\(a[i]\)来说的一段\(gcd\)相同的区间,而且下一个区间的左边界就成了\(r+1\)\(gcd\)值也变成\(gcd(x,a[r+1])\),继续二分查找该\(gcd\)的右边界,一直到找到第\(n\)个数为止。这样就得到了从\(i\)开始的\(gcd\)相同的各个区间。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxx = 10010;
struct node{int l,r,x;};
vector<node>v[maxx]; //保存从i开始的gcd相同的各个区间
struct Q
{
    int l,r,id;
    bool operator < (const Q &t)const
    {
        return l>t.l;
    }
}q[maxx];
int a[maxx],n,m;
int t[maxx<<2];
LL ans[maxx];
struct tree
{
    LL t[maxx<<2],lazy[maxx<<2];
    void build(int l,int r,int rt)
    {
        t[rt]=lazy[rt]=0;
        if(l==r)return;
        int mid=(l+r)/2;
        build(l,mid,rt*2);
        build(mid+1,r,rt*2+1);
    }
    void pushdown(int l,int r,int rt)
    {
        if(lazy[rt])
        {
            t[rt*2]+=lazy[rt]*l;
            t[rt*2+1]+=lazy[rt]*r;
            lazy[rt*2]+=lazy[rt];
            lazy[rt*2+1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void update(int l,int r,int p,int q,int c,int rt)
    {
        if(p<=l&&r<=q)
        {
            t[rt]+=1LL*(r-l+1)*c;
            lazy[rt]+=c;
            return;
        }
        int mid=(l+r)/2;
        pushdown(mid-l+1,r-mid,rt);
        if(p<=mid)update(l,mid,p,q,c,rt*2);
        if(q>mid)update(mid+1,r,p,q,c,rt*2+1);
        t[rt]=t[rt*2]+t[rt*2+1];
    }
    LL query(int l,int r,int p,int q,int rt)
    {
        if(p==l&&q==r)return t[rt];
        int mid=(l+r)/2;
        pushdown(mid-l+1,r-mid,rt);
        if(q<=mid)return query(l,mid,p,q,rt*2);
        else if(p>mid)return query(mid+1,r,p,q,rt*2+1);
        else return query(l,mid,p,mid,rt*2)+query(mid+1,r,mid+1,q,rt*2+1);
    }
}bt;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        t[rt]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    t[rt]=gcd(t[rt*2],t[rt*2+1]);
}
int query(int l,int r,int p,int q,int rt)
{
    if(l==p&&r==q)return t[rt];
    int mid=(l+r)/2;
    if(q<=mid)return query(l,mid,p,q,rt*2);
    else if(p>mid)return query(mid+1,r,p,q,rt*2+1);
    else return gcd(query(l,mid,p,mid,rt*2),query(mid+1,r,mid+1,q,rt*2+1));
}
int Search(int a,int b,int x)
{
    int l=a,r=b,ans;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(query(1,n,a,mid,1)>=x)l=mid+1,ans=mid;
        else r=mid-1;
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,n,1);
        node tmp;
        for(int i=1;i<=n;i++)
        {
            v[i].clear();
            int l=i,r,x=a[i];
            while(l<=n)
            {
                r=Search(i,n,x);
                tmp=node{l,r,x};
                v[i].push_back(tmp);
                x=gcd(x,a[r+1]);
                l=r+1;
            }
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
        sort(q+1,q+1+m);
        bt.build(1,n,1);
        int l=n;
        for(int i=1;i<=m;i++)
        {
            while(l>=q[i].l)
            {
                for(int j=0;j<v[l].size();j++)
                    bt.update(1,n,v[l][j].l,v[l][j].r,v[l][j].x,1);
                l--;
            }
            ans[q[i].id]=bt.query(1,n,q[i].l,q[i].r,1);
        }
        for(int i=1;i<=m;i++)
            printf("%lld\n",ans[i]);
    }
    return 0;
}
posted @ 2020-04-07 08:50  灰灰烟影  阅读(143)  评论(0编辑  收藏  举报