The sum of gcd(莫隊:維護區間GCD和)
阿新 • • 發佈:2018-11-27
原題: 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
詳細步驟:
- 當前預處理以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]);
}
}
}