题解:
还是比较水的一道题
首先可以发现每个数最多被除log次,所以有连续一段相同
然后我想的是变成矩形统计前缀和问题用主席树来维护
然后发现这题很卡空间
qwq acm依旧很多64mb的题
首先比较重要的一点是
这题如果不用标记永久化
需要用到down
主席树down需要新开节点(随意yy一下就知道了)
当然为了节省空间我们选择标记永久化
这时候我们就要记录两个标记
一个代表不用传递到儿子的data
一个代表要传递到儿子的lazy
至于为什么自己想一下就知道了
另外这题的方法应该是将询问离线(离线很重要啊)
然后把每个询问拆成两个,sort一遍再做
这样就是线段树的空间了
正解:
另外这题应该还有一个做法,就是直接对整个序列建线段树是在线的(我觉得这个应该才算正解)
然后维护每个区间中子区间gcd的和,和边界区间对应的log个数值,以及对应个数
然后区间合并显然可以通过这些信息来维护
而查询的时候先找出对应区间,用和updata一样的方法维护答案
不过这样updata的时候应该是logn^2的
所以时间应该是nlogn^3的
线段树很多时候就是像这样利用区间合并 下次应该要注意一下
代码:
#include <bits/stdc++.h> using namespace std; #define ll long long #define IL inline #define rint register int #define rep(i,h,t) for (rint i=h;i<=t;i++) #define dep(i,t,h) for (rint i=t;i>=h;i--) #define mid ((h+t)/2) IL int max(int x,int y) { if (x>y) return(x); else return(y); } IL int min(int x,int y) { if (x<y) return(x); else return(y); } IL void swap(int &x,int &y) { int tmp=x;x=y,y=tmp; } struct re{ int a,b; }; vector<re> ve,ve1; const int N=1.1e4; const int N2=3e6; int n,m,a[N],root[N],cnt,ls[N2],rs[N2],lazy[N2]; ll data[N2]; int gcd(int x,int y) { if(!y) return(x); return(gcd(y,x%y)); } void change(int last,int &x,int h,int t,int h1,int t1,int k) { x=++cnt; ls[x]=ls[last]; rs[x]=rs[last]; lazy[x]=lazy[last]; data[x]=data[last]; data[x]+=1ll*k*(min(t,t1)-max(h,h1)+1); if (h1<=h&&t<=t1) { lazy[x]+=k; return; } if (h1<=mid) change(ls[last],ls[x],h,mid,h1,t1,k); if (mid<t1) change(rs[last],rs[x],mid+1,t,h1,t1,k); } ll query2(int x,int h,int t,int h1,int t1,int k) { if (h1<=h&&t<=t1) return(data[x]+k*(t-h+1)); ll ans=0; if (h1<=mid) ans+=query2(ls[x],h,mid,h1,t1,k+lazy[x]); if (mid<t1) ans+=query2(rs[x],mid+1,t,h1,t1,k+lazy[x]); return(ans); } IL ll query(int x,int h,int t,int h1,int t1) { if (h1>t1) return(0); return(query2(x,h,t,h1,t1,0)); } int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); int T; scanf("%d",&T); while (scanf("%d",&n)!=EOF) { rep(i,1,n) scanf("%d",&a[i]); cnt=0; memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs)); memset(lazy,0,sizeof(lazy)); ve.clear(); rep(i,1,n) { ve1.clear(); int tmp=a[i],last=i; root[i]=root[i-1]; ve1.push_back((re){i,tmp}); int len=ve.size()-1; rep(j,0,len) { int tmp2=tmp; tmp=gcd(tmp,ve[j].b); if (tmp2!=tmp) { ve1.push_back((re){ve[j].a,tmp}); change(root[i],root[i],1,n,ve[j].a+1,last,tmp2); last=ve[j].a; } } change(root[i],root[i],1,n,1,last,tmp); ve.swap(ve1); } scanf("%d",&m); rep(i,1,m) { int l,r; scanf("%d%d",&l,&r); printf("%lld ",query(root[r],1,n,1,r)-query(root[r],1,n,1,l-1)); } } return 0; }