题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=5381
题意
给你(n)个数,(q)次查询,每次询问(l,r),输出(f(l,r)=sum_{i=l}^rsum_{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
",ans[i]);
}
return 0;
}