gcd运算的时候 如果之前的保持不变 新加入一个数进行gcd运算 得到的答案只会越来越小
这题要我们求[l.r]中所有子区间的gcd值之和 我们就要尽量保存子区间的gcd在合并区间的时候以尽可能快的速度合并
我们可以知道gcd的数量是log级别的
所以我们可以保存Rg[32],Rn[32],Rc,分别记录的是[i,r](i>=l)的不同gcd值,不同gcd值的数量,有多少种不同的gcd值
Lg[32],Ln[32],Lc同上
所以在合并两个区间ab的时候
我们可以先遍历 a.Rg b.Lg两两进行gcd运算 这一过程将[l,r]中所有中间部分的gcd值进行了更新
然后取出a.Lg[a.Lc-1]也即a整个区间的gcd值,然后遍历b.Lg 进行gcd运算 这一过程相当于将从l开始的gcd值进行了更新
然后取出b.Rg[b.Lc-1]进行如上操作
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 10010;
struct node
{
LL sum;
int Lg[32],Rg[32],Ln[32],Rn[32],Lc,Rc;//rg、lg记录的是gcd值 lc、rc记录的是不同gcd值的数量 lnrn记录的是每个gcd值的数量
} tree[maxn<<2];
node pushup(const node &a,const node &b) //合并两个区间
{
node ret;
ret.sum=a.sum+b.sum;//之前两个区间自己的先加一下
for(int i=0;i<a.Rc;++i)
for(int j=0;j<b.Lc;++j)
ret.sum+=__gcd(a.Rg[i],b.Lg[j])*1LL*a.Rn[i]*b.Ln[j];//更新函数值
for(int i=0;i<b.Rc;++i) //更新gcd储值
{
ret.Rg[i]=b.Rg[i];
ret.Rn[i]=b.Rn[i];
}
for(int i=0;i<a.Lc;++i)
{
ret.Lg[i]=a.Lg[i];
ret.Ln[i]=a.Ln[i];
}
int d=b.Rg[b.Rc-1],p=b.Rc;//d赋值为b.Rg[b.Rc-1]即为右区间的最小值 也即为右区间的所有数的gcd
for(int i=0;i<a.Rc;++i)
{
int tmp=__gcd(a.Rg[i],d);
if(tmp==ret.Rg[p-1]) ret.Rn[p-1]+=a.Rn[i];
else
{
ret.Rg[p]=tmp;
ret.Rn[p++]=a.Rn[i];
}
}
ret.Rc=p;
d = a.Lg[a.Lc-1],p=a.Lc;//同上
for(int i=0;i<b.Lc;++i)
{
int tmp=__gcd(d,b.Lg[i]);
if(tmp==ret.Lg[p-1]) ret.Ln[p-1]+=b.Ln[i];
else
{
ret.Ln[p]=b.Ln[i];
ret.Lg[p++]=tmp;
}
}
ret.Lc=p;
return ret;
}
void build(int L,int R,int v)
{
if(L==R)
{
tree[v].Lc=tree[v].Rc=1;
tree[v].Ln[0]=tree[v].Rn[0]=1;
scanf("%d",&tree[v].Lg[0]);
tree[v].sum=tree[v].Rg[0]=tree[v].Lg[0];//L==R时 区间的函数值为读入的数
return;
}
int mid=(L+R)>>1;
build(L,mid,v<<1);
build(mid+1,R,v<<1|1);
tree[v]=pushup(tree[v<<1],tree[v<<1|1]);//pushup的作用就是合并两条线段
}
void query(int L,int R,int lt,int rt,int v)
{
if(lt<=L&&rt>=R)
{
if(lt==L) tree[0]=tree[v];
else tree[0]=pushup(tree[0],tree[v]);
return;
}
int mid=(L+R)>>1;
if(lt<=mid) query(L,mid,lt,rt,v<<1);
if(rt>mid) query(mid+1,R,lt,rt,v<<1|1);
}
int main()
{
int T,n,m,x,y;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&x,&y);
query(1,n,x,y,1);
printf("%I64d\n",tree[0].sum);
}
}
return 0;
}