题目描述
给你一个长为n的序列a
m次查询
每次查询一个区间的所有子区间的gcd的和mod1e9+7的结果
输入描述:
第一行两个数n,m
之后一行n个数表示a
之后m行每行两个数l,r表示查询的区间
输出描述:
对于每个询问,输出一行一个数表示答案
输入
5 7 30 60 20 20 20 1 1 1 5 2 4 3 4 3 5 2 5 2 3
输出
30 330 160 60 120 240 100
说明
[1,1]的子区间只有[1,1],其gcd为30
[1,5]的子区间有:
[1,1]=30,[1,2]=30,[1,3]=10,[1,4]=10,[1,5]=10
[2,2]=60,[2,3]=20,[2,4]=20,[2,5]=20
[3,3]=20,[3,4]=20,[3,5]=20
[4,4]=20,[4,5]=20
[5,5]=20
总共330
[2,4]的子区间有:
[2,2]=60,[2,3]=20,[2,4]=20
[3,3]=20,[3,4]=20
[4,4]=20
总共160
[3,4]的子区间有:
[3,3]=20,[3,4]=20
[4,4]=20
总共60
[3,5]的子区间有:
[3,3]=20,[3,4]=20,[3,5]=20
[4,4]=20,[4,5]=20
[5,5]=20
总共120
[2,5]的子区间有:
[2,2]=60,[2,3]=20,[2,4]=20,[2,5]=20
[3,3]=20,[3,4]=20,[3,5]=20
[4,4]=20,[4,5]=20
[5,5]=20
总共240
[2,3]的子区间有:
[2,2]=60,[2,3]=20
[3,3]=20
总共100
http://blog.csdn.net/fsahfgsadhsakndas/article/details/52650026 //详解
我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)
设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v
【今天的主要内容】
我们可以实现NlogN时间的“单点修改,区间查询”,“区间修改,单点查询”,其实后者就是前者的一个变形,要明白树状数组的本质就是“单点修改,区间查询”
怎么实现“区间修改,区间查询”呢?
观察式子:
a[1]+a[2]+...+a[n]
= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])
= n*c[1] + (n-1)*c[2] +... +c[n]
= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n]) (式子①)
那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]
每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变
那么
式子①
=n*sigma(c,n) - sigma(c2,n)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+88; const int mod=1e9+7; int n,m,a[N],v[250],pos[250]; long long f[N],g[N],ans[N]; struct node{ int l,r,id; bool operator < (const node &A)const{ return r<A.r; } }q[N]; void add(int x,int y){ for(int i=x;i<=n;i+=i&(-i)) f[i]+=y,g[i]+=1LL*(x-1)*y; } long long query(int u){ long long ans=0; for(int i=u;i;i-=i&(-i)) ans=(ans+1LL*u*f[i]-g[i])%mod; return ans; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",a+i); for(int i=1;i<=m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; sort(q+1,q+m+1); int tot=0,now; for(int i=1,p=1;i<=m;++i) { while(p<=q[i].r) { for(int j=1;j<=tot;++j) v[j]=__gcd(v[j],a[p]); v[++tot]=a[p],pos[tot]=p; now=tot,tot=0; for(int j=1;j<=now;++j) if(v[j]!=v[j-1]) v[++tot]=v[j],pos[tot]=pos[j]; for(int j=1;j<tot;++j) add(pos[j],v[j]),add(pos[j+1],-v[j]); add(pos[tot],v[tot]),add(p+1,-v[tot]); ++p; } ans[q[i].id]=query(q[i].r)-query(q[i].l-1); } for(int i=1;i<=m;++i) printf("%lld\n",(ans[i]+mod)%mod); }