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;
}