#4702. gcd
题解
考虑从大到小枚举 $gcd$ ,假设 $gcd$ 的倍数出现的位置从小到大为 $p_1,p_2,...,p_{t-1},p_t$ ,那它影响的区间为 $[1,p_{t-1}-1],[p_1+1,p_t-1],[p_2+1,n]$
考虑对每个点 $l$ 维护一个右端点 $r$ 表示 $[l,u](l\le u \le r)$ 都被影响过了,每次计算答案的时候考虑用总区间数减去被影响过区间数,发现随着 $l$ 的增大 $r$ 是单调递增的,于是线段树上二分即可
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=2e5+5; int T,n,t,a[N],b[N],m,h[N<<2],tg[N<<2],g[N]; LL s,f[N<<2]; struct O{int k,l,r;}S[25]; #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) void up(int k){ f[k]=f[Ls]+f[Rs]; h[k]=min(h[Ls],h[Rs]); } void push(int k,int l,int r,int v){ h[k]=tg[k]=v;f[k]=1ll*(r-l+1)*v; } void down(int k,int l,int r){ push(Ls,l,mid,tg[k]); push(Rs,mid+1,r,tg[k]); tg[k]=0; } void build(int k,int l,int r){ tg[k]=0;if (l==r){f[k]=h[k]=l-1;return;} build(Ls,l,mid);build(Rs,mid+1,r);up(k); } void upd(int k,int l,int r,int L,int R,int v){ if (L<=l && r<=R) return push(k,l,r,v); if (tg[k]) down(k,l,r); if (mid>=L) upd(Ls,l,mid,L,R,v); if (mid<R) upd(Rs,mid+1,r,L,R,v); up(k); } void find(int k,int l,int r,int L,int R){ if (L<=l && r<=R){ S[++t]=(O){k,l,r};return; } if (tg[k]) down(k,l,r); if (mid>=L) find(Ls,l,mid,L,R); if (mid<R) find(Rs,mid+1,r,L,R); } int get(int k,int l,int r,int v,LL &w){ if (l==r){ if (h[k]>=v) return l-1; else{w+=f[k];return l;} } if (tg[k]) down(k,l,r); if (h[Rs]>=v) return get(Ls,l,mid,v,w); else{ w+=f[Ls]; return get(Rs,mid+1,r,v,w); } } void work(int l,int r,int v){ if (l>r) return; LL x=1ll*(r-l+1)*(r-l+2)/2; t=0;find(1,1,n,l,r); LL y=0;int u=r; for (int i=1;i<=t;i++){ if (h[S[i].k]>=r){ if (i>1) u=get(S[i-1].k,S[i-1].l,S[i-1].r,r,y); else u=l-1; break; } if (i>1) y+=f[S[i-1].k]; if (i==t) u=get(S[i].k,S[i].l,S[i].r,r,y); } x-=(y+1ll*(r-u)*r-1ll*(l+r)*(r-l+1)/2+(r-l+1)); if (l<=u) upd(1,1,n,l,u,r);s+=x*v; } void work(){ scanf("%d",&n); build(1,1,n);m=0;s=0; for (int i=1;i<=n;i++) scanf("%d",&a[i]), b[a[i]]=i,m=max(m,a[i]); for (int v,i=m;i;i--){ v=0; for (int j=i;j<=m;j+=i) if (b[j]) g[++v]=b[j]; if (v<2) continue; sort(g+1,g+v+1); work(g[1]+1,g[v]-1,i); work(1,g[v-1]-1,i); work(g[2]+1,n,i); } for (int i=1;i<=m;i++) b[i]=0; printf("%lld\n",s); } int main(){ for (scanf("%d",&T);T--;work()); return 0; }