The sum of gcd(莫队:维护区间GCD和)

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

题意:

n数组,q次询问,每次询问l,r,输出l到r所有子区间的gcd和

解析:

首先是莫队,排序了一下询问。接下来的问题是维护询问区间的问题。假设当前查询的是l~r-1,需要扩展到l~r。那么我们加上的是a[r]对l~r这个区间的贡献,也就是要算出gcd(l~r)+gcd(l+1~r)+gcd(l+2~r)...gcd(r~r)

这个需要O(logn)处理出来


显然,这么多个区间的gcd值只有logn种可能性,且随着区间的变长递减。那么我们只需要预处理出以i为右端点的区间的gcd的可能并记录位置,就可以logn维护查询区间了


详细步骤:

  1. 当前预处理以i为右端点的所有区间,放入vr[i]中,用pair存,first为gcd值,second为最后一次(最往左)gcd为此值的pos
  2. 首先push_back {a[i],i}
  3. 遍历vr[i-1]。因为vr[i-1]中存的也是一段相同gcd区间的信息,i直接连上去即可
  4. 假设得出gcd相同,则改变second(位置),如果不同则push_back {gcd,pos}
  5. 预处理结果可用代码中的注释部分查看
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int Len;

struct node{
    int l,r,id;
    bool operator<(const node &a)const{
        return l/Len<a.l/Len||l/Len==a.l/Len&&r<a.r;
    }
}e[10009];

int a[10009];
int l,r;LL now;

typedef pair<int,int> pill;
vector<pill>vl[10009],vr[10009];
void init(int n){
    for(int i=0;i<=n;i++)vl[i].clear(),vr[i].clear();
    for(int i=1;i<=n;i++){
        int gcd=a[i];
        vr[i].push_back({a[i],i});//gcd pos
        vector<pill>&pre=vr[i-1];
        for(int j=0;j<pre.size();j++){
            int v=pre[j].first,pos=pre[j].second;
            gcd=__gcd(gcd,v);
            if(gcd==vr[i].back().first){
                vr[i].back().second=pos;
            }
            else{
                vr[i].push_back({gcd,pos});
            }
        }
    }
        //for(int i=1;i<=n;i++){printf("%d : ",i); for(int j=0;j<vr[i].size();j++){printf("(gcd=%d pos=%d) "
        //,vr[i][j].first,vr[i][j].second);}printf("\n"); }printf("\n");
    for(int i=n;i>=1;i--){
        int gcd=a[i];
        vl[i].push_back({a[i],i});//gcd pos
        vector<pill>&pre=vl[i+1];
        for(int j=0;j<pre.size();j++){
            int v=pre[j].first,pos=pre[j].second;
            gcd=__gcd(gcd,v);
            if(gcd==vl[i].back().first){
                vl[i].back().second=pos;
            }
            else{
                vl[i].push_back({gcd,pos});
            }
        }
    }
        //for(int i=1;i<=n;i++){printf("%d : ",i);for(int j=0;j<vl[i].size();j++){printf("(gcd=%d pos=%d) "
        //,vl[i][j].first,vl[i][j].second); }printf("\n");}printf("\n");
}

void upl(int x,int f){//l to r
    LL sum=0;
    vector<pill>&V=vl[l];
    for(int i=0;i<V.size();i++){
        int v=V[i].first,p=V[i].second,pp=l-1;
        if(i)pp=V[i-1].second;
        if(p<=r){
            sum+=(LL)(p-pp)*v;
        }
        else{
            sum+=(LL)(r-pp)*v;
            break;
        }
    }
    now+=sum*f;
}

void upr(int x,int f){//r to l
    LL sum=0;
    vector<pill>&V=vr[r];
    for(int i=0;i<V.size();i++){
        int v=V[i].first,p=V[i].second,pp=r+1;
        if(i)pp=V[i-1].second;
        if(p>=l){
            sum+=(LL)(pp-p)*v;
        }
        else{
            sum+=(LL)(pp-l)*v;
            break;
        }
    }
    now+=sum*f;
}

LL ans[10009];
int main(){
    int t;scanf("%d",&t);
    while(t--){
        int n,q;
        scanf("%d",&n);
        Len=int(sqrt(n)+0.5);
        for(int i=1;i<=n;i++)scanf("%d",a+i);
        init(n);
        scanf("%d",&q);
        for(int i=1;i<=q;i++){
            e[i].id=i;
            scanf("%d%d",&e[i].l,&e[i].r);
        }
        sort(e+1,e+1+q);
        l=e[1].l,r=e[1].l-1;
        now=0;
        for(int i=1;i<=q;i++){
            while(r<e[i].r)r++,upr(r,1);
            while(l>e[i].l)l--,upl(l,1);
            while(r>e[i].r)upr(r,-1),r--;
            while(l<e[i].l)upl(l,-1),l++;
            ans[e[i].id]=now;
        }
        for(int i=1;i<=q;i++){
            printf("%lld\n",ans[i]);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/83623234
今日推荐