学习了下莫队算法,用左端点所在块分割排序排序后,设n是区间范围,每个块n^0.5的大小,
对于右端点R的更新,同一块内,R是从小到大,O(n)*块数=O(n^1.5),相邻块O(n)*块数=O(n^1.5)
对于左端点的更新,同一块内,每次最多 O(n^0.5),不同块,最多2*n^0.5次,一共*m次询问
大概加一下是O(n^1.5)的复杂度,这个题目是n=m是10000,大概是100万的复杂度
然后因为以一个点为左端点或者右端点的gcd是单调的,而且不同种类的gcd最多log(ai)种,以每段区间除以最小的因子2算的话。
每次使用莫队算法移动区间的更新就是遍历每一种gcd所在的位置,大概就30次,所以最后复杂度是 O(n^1.5)*30 =3000万次,不会超时的。
#include<stdio.h> #include<vector> #include<cmath> #include<stdlib.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; const int N=1e4+10; int n; int a[N]; void rd(int&x){ char ch; for(ch=getchar();ch<'0' || ch>'9';ch=getchar()); x=0; for(;ch>='0' && ch<='9';ch=getchar()) x=x*10+ch-'0'; } int gcd(int x,int y){ return y==0?x:gcd(y,x%y); } int block; struct node{ int l,r,id; bool operator <(const node & x) const{ if(l/block==x.l/block) return r<=x.r; else return l/block<x.l/block; } }vec[N]; long long an[N]; long long ans; int L,R,q; struct no{ int idx,g; no(int _i,int _g):idx(_i),g(_g){} }; vector<no> l[N],r[N];//l[i]表示以第i个点为右端点左边不同gcd段的位置,r同理 void init(){ for(int i=1;i<=n;i++) l[i].clear(),r[i].clear(); l[1].push_back(no(1,a[1]));//最左边的点只有一段,就是自己 for(int i=2;i<=n;i++){ int cur=a[i],L=i; for(int j=0;j<l[i-1].size();j++){//第i个点根据i-1个点的值进行更新,进行每一段的更新只可能有两种情况,与上一段gcd相等,比上一段gcd小 no v=l[i-1][j];int tmp=gcd(v.g,cur); if(cur!=tmp) l[i].push_back(no{L,cur}),cur=tmp;//比上一段gcd小,就把当前这段放入 L=v.idx; } l[i].push_back(no(L,cur)); } r[n].push_back(no{n,a[n]}); for(int i=n-1;i>=1;i--){ int cur=a[i],R=i; for(int j=0;j<r[i+1].size();j++){ no v=r[i+1][j];int tmp=gcd(cur,v.g); if(tmp!=cur) r[i].push_back(no{R,cur}),cur=tmp; R=v.idx; } r[i].push_back(no{R,cur}); } } long long get(int L,int R,char ch){//计算L--R的值,以ch为端点 long long res=0; if(ch=='l'){ int pos=L; for(int i=0;i<r[L].size();i++){ no v=r[L][i]; if(pos>R) break; if(v.idx<R) res+=(long long)v.g*(v.idx-pos+1);//当前这一段还远远没有到另一个端点 else res+=(long long)v.g*(R-pos+1);//当前这一段已经超出了另一个端点 pos=v.idx+1; } }else{ int pos=R; for(int i=0;i<l[R].size();i++){ no v=l[R][i]; if(pos<L) break; if(v.idx>L)res+=(long long)v.g*(pos-v.idx+1); else res+=(long long)v.g*(pos-L+1); pos=v.idx-1; } } return res; } void solve(){ init(); ans=0;L=1;R=0; for(int i=1;i<=q;i++){ int ll=vec[i].l,rr=vec[i].r,id=vec[i].id; while(ll<L){L--;ans+=get(L,R,'l');} while(R<rr){R++;ans+=get(L,R,'r');} while(ll>L){ans-=get(L,R,'l');L++;} while(R>rr){ans-=get(L,R,'r');R--;} an[id]=ans; } for(int i=1;i<=q;i++) printf("%I64d\n",an[i]); } int main(){ #ifndef ONLINE_JUDGE freopen("aaa","r",stdin); #endif int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i); scanf("%d",&q); for(int i=1;i<=q;i++){ scanf("%d%d",&vec[i].l,&vec[i].r); vec[i].id=i; } block=(int)(sqrt(n)+0.5);//块数 sort(vec+1,vec+q+1);//按块排个序 solve(); } return 0; }