1. 程式人生 > >The sum of gcd(莫隊:維護區間GCD和)

The sum of gcd(莫隊:維護區間GCD和)

原題: 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維護查詢區間了


詳細步驟:

  1. 當前預處理以i為右端點的所有區間,放入vr[i]中,用pair存,first為gcd值,second為最後一次(最往左)gcd為此值的pos
  2. 首先push_back {a[i],i}
  3. 遍歷vr[i-1]。因為vr[i-1]中存的也是一段相同gcd區間的資訊,i直接連上去即可
  4. 假設得出gcd相同,則改變second(位置),如果不同則push_back {gcd,pos}
  5. 預處理結果可用程式碼中的註釋部分檢視
#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]); } } }