Hdu5381-The sum of gcd(莫队)

题意我就不说了
 
解析: 莫队,先预处理出以i为右端点的区间的gcd值,有一些连续的区间的gcd值是相同的,比如[j,i],[j+1,i],[j+2,i]的gcd值是相同的,我们可以把[j,j+2]这个
区间保存下来。同时也预处理出以i为左端点的,最后用莫队算法。详见代码实现。
 
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=10005;
typedef __int64 LL;
int N,M,A[maxn],block;
struct Ques
{
    int x,y,id;
    Ques(int x=0,int y=0,int id=0):x(x),y(y),id(id){}
    bool operator < (const Ques& t) const
    {
        int a=x/block,b=t.x/block;  //排序
        if(a!=b) return a<b;
        return y<t.y;
    }
}ques[maxn];
int gcd(int a,int b){ return b==0?a:gcd(b,a%b); }
struct node
{
    int id,g;
    node(int id=0,int g=0):id(id),g(g){}
};
vector<node> vl[maxn],vr[maxn];
void GetLe()
{
    for(int i=1;i<=N;i++)  //得到以i为右端点连续区间的gcd值
    {
        if(i==1){ vl[i].push_back(node(i,A[i])); continue; }
        int g=A[i],id=i;
        int Size=vl[i-1].size();
        for(int j=0;j<Size;j++)
        {
            node& t=vl[i-1][j];
            int ng=gcd(t.g,g);
            if(ng!=g) vl[i].push_back(node(id,g));
            g=ng; id=t.id;
        }
        vl[i].push_back(node(id,g));
    }
}
void GetRi()  //同理
{
    for(int i=N;i>=1;i--)
    {
        if(i==N){ vr[i].push_back(node(i,A[i])); continue; }
        int g=A[i],id=i;
        int Size=vr[i+1].size();
        for(int j=0;j<Size;j++)
        {
            node& t=vr[i+1][j];
            int ng=gcd(t.g,g);
            if(ng!=g) vr[i].push_back(node(id,g));
            g=ng; id=t.id;
        }
        vr[i].push_back(node(id,g));
    }
}
LL WorkLe(int x,int y)
{
    int Size=vl[y].size();
    int ny=y;
    LL ret=0;
    for(int i=0;i<Size;i++)
    {
        node& t=vl[y][i];
        if(t.id>=x)
        {
            ret+=(LL)t.g*(ny-t.id+1);
            ny=t.id-1; //跳过去
        }
        else{ ret+=(LL)t.g*(ny-x+1); break; }
    }
    return ret;
}
LL WorkRi(int x,int y)
{
    int nx=x;
    LL ret=0;
    int Size=vr[x].size();
    for(int i=0;i<Size;i++)
    {
        node& t=vr[x][i];
        if(t.id<=y)
        {
            ret+=(LL)t.g*(t.id-nx+1);
            nx=t.id+1;
        }
        else { ret+=(LL)t.g*(y-nx+1); break; }
    }
    return ret;
}
LL ans[maxn];
void solve()
{
    for(int i=0;i<=N;i++) vl[i].clear(),vr[i].clear();
    block=(int)sqrt(N+0.5);  //分块
    sort(ques,ques+M);  //排序
    GetLe(); //得到左边连续相同的gcd区间
    GetRi(); //得到右边连续相同的gcd区间
    int x=1,y=0;
    LL ret=0;
    for(int i=0;i<M;i++)  //莫队的主要实现部分
    {
        Ques& q=ques[i];
        while(y<q.y){ y++; ret+=WorkLe(x,y); }
        while(y>q.y){ ret-=WorkLe(x,y); y--; }
        while(x<q.x){ ret-=WorkRi(x,y); x++; }
        while(x>q.x){ x--; ret+=WorkRi(x,y); }
        ans[q.id]=ret; //保存答案
    }
    for(int i=0;i<M;i++) printf("%lld
",ans[i]); //输出
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i=1;i<=N;i++) scanf("%d",&A[i]);//输入
        scanf("%d",&M);
        int x,y;
        for(int i=0;i<M;i++)
        {
            scanf("%d%d",&x,&y);
            ques[i]=Ques(x,y,i);  //离线保存查询
        }
        solve();
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wust-ouyangli/p/5706230.html