【HDU5869】 Different GCD Subarray Query 题解 (线段树维护区间GCD)
题目大意:求区间$[L,R]$中所有子区间产生的最大公因数的个数。
-------------------------
对于$gcd$,我们知道$gcd(a,b,c)=gcd(gcd(a,b),c)$。所以我们可以利用$gcd$的传递性来求区间的$gcd$。如果$gcd$相同,那么保留下来位置相对靠右的那一个,这与我们查询的方式有关。我们在查询时是$O(n)$正向遍历的,询问的区间按照右端点进行关键字排序,每次维护一个新的$gcd$最靠右的位置并让这个位置+1,让之前的位置-1即可。
因为每次$gcd$结果至少除以2,所以复杂度降成了$\log$级别的。总复杂度$O(n\log n)$。
小细节:线段树建树的时候一定要从$0$开始建树,因为$pre[gcd]$是有可能等于$0$的。
代码:
#include<bits/stdc++.h> #define int long long using namespace std; const int maxn=100005; int a[maxn],n,T; struct node { int p,g; }; struct qq { int l,r,id; }q[maxn]; struct t { int l,r,sum; }tree[maxn<<2]; vector<node> v[maxn]; int pre[10*maxn]; int res[maxn]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } int gcd(int a,int b) { if (!b) return a; return gcd(b,a%b); } int cmp(qq a,qq b) { return a.r<b.r; } inline void build(int index,int l,int r) { tree[index].l=l; tree[index].r=r; if (l==r) { tree[index].sum=0; return; } int mid=(l+r)>>1; build(index*2,l,mid); build(index*2+1,mid+1,r); } void change(int index,int pos,int x) { if (tree[index].l==tree[index].r) { tree[index].sum+=x; return; } int mid=(tree[index].l+tree[index].r)>>1; if (mid>=pos) change(index*2,pos,x); else change(index*2+1,pos,x); tree[index].sum=tree[index*2+1].sum+tree[index*2].sum; } int query(int index,int l,int r) { if (l<=tree[index].l&&tree[index].r<=r) { return tree[index].sum; } int ans=0,mid=(tree[index].l+tree[index].r)>>1; if (mid>=l) ans+=query(index*2,l,r); if (mid<r) ans+=query(index*2+1,l,r); return ans; } signed main() { n=read(),T=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=T;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; build(1,0,n); for (int i=1;i<=n;i++) { int x=a[i],y=i,k=v[i-1].size(); for (int j=0;j<k;j++) { int u=v[i-1][j].g; int gg=gcd(u,x); if (x!=gg) { v[i].push_back((node){y,x}); y=v[i-1][j].p; x=gg; } } v[i].push_back((node){y,x}); } sort(q+1,q+T+1,cmp); int now=1; for (int i=1;i<=n;i++) { int k=v[i].size(); for (int j=0;j<k;j++) { int pp=v[i][j].p; int gg=v[i][j].g; change(1,pre[gg],-1); pre[gg]=pp; change(1,pp,1); } while (q[now].r==i) { int id=q[now].id; res[id]=query(1,q[now].l,q[now].r); now++; if (now>T) break; } } //for (int i=1;i<=n<<2;i++) cout<<tree[i].sum<<endl; for (int i=1;i<=T;i++) printf("%lld\n",res[i]); return 0; }