原题: 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维护查询区间了
详细步骤:
- 当前预处理以i为右端点的所有区间,放入vr[i]中,用pair存,first为gcd值,second为最后一次(最往左)gcd为此值的pos
- 首先
push_back {a[i],i}
- 遍历vr[i-1]。因为vr[i-1]中存的也是一段相同gcd区间的信息,i直接连上去即可
- 假设得出gcd相同,则改变
second
(位置),如果不同则push_back {gcd,pos}
- 预处理结果可用代码中的注释部分查看
#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]);
}
}
}