Problem Description
Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
First line has one integers n
Second line has n integers Ai
Third line has one integers Q,the number of questions
Next there are Q lines,each line has two integers l,r
1≤T≤3
1≤n,Q≤10^4
1≤ai≤10^9
1≤l<r≤n
Output
For each question,you need to print f(l,r)
Sample Input
2
5
1 2 3 4 5
3
1 3
2 3
1 4
4
4 2 6 9
3
1 3
2 4
2 3
Sample Output
9
6
16
18
23
10
题意:给出n个数,按要求求值。
思路:
以下来自:https://blog.csdn.net/animalcoder/article/details/80310579
1e4 考虑莫队,每次[l,r]转移到[l-1,r]需要加上sum=gcd[l-1,l-1]+gcd[l-1,l]+gcd[l-1,l+1]....+gcd[l-1,r];
由于gcd特性,gcd[l-1,l-1],gcd[l-1,l],gcd[l-1,l+1]....gcd[l-1,r]是递减的,且值的数目不会超过logn 。。。(小本本记下来)
莫队要logn求出sum: 所以我们需要维护出lft 跟 rft 维护出累计gcd值,跟转折下标 (lft定义看下面)
计算转移: [l,r]转移到[l-1,r]我们只要暴力遍历lft 利用转折下标的差值*该累计gcd 计算sum
lft定义: 比如lft[2]:固定左端点2 右端点滑动2~n 对应的累计gcd值与转折下标
原数组下标: 2 3 4 5 6 7 8 9 .... 13
累计gcd: 8 8 8 8 8 4 4 2 2 2 1 1 (gcd(a2,a3),gcd(a2,a3,a4)......)
转折下标 ↑ ↑ ↑ ↑
lft: lft[2][0]=(6,8),lft[2][1]=(8,4),lft[2][2]=(11,2),lft[2][3]=(13,1)
lft内存会不会炸?:固定左端点2 顶多logn个gcd值,所以内存不会炸
lft维护方法:与hdu5726维护的方法一样,二分+RMQ求出转移下标跟累计gcd值
代码思路:
A 预处理ST
B 预处理lft rft
C 离线莫队,计算转移
代码:
const int maxn=1e4+15;
struct mo
{
int id,ql,qr;
}q[maxn];
int bel[maxn];
int col[maxn];
ll res[maxn];
struct node
{
int pos,gcd;
node(){}
node(int pos,int gcd):pos(pos),gcd(gcd){};
};
vector<node>lft[maxn];
vector<node>rft[maxn];
int dp[maxn][19];
int logg[maxn];
int a[maxn];
void ST(int n)//维护区间gcd RMQ
{
for(int i=1;i<=n;i++) dp[i][0]=a[i],logg[i]=log2(i);
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
int x=dp[i][j-1],y=dp[i+(1<<j-1)][j-1];
dp[i][j]=__gcd(x,y);
}
}
}
int RMQ(int l,int r)
{
int len=logg[r-l+1];
int x=dp[l][len],y=dp[r-(1<<len)+1][len];
return __gcd(x,y);
}
int lsearch(int l,int r,int qq,int i)//固定左端点
{
int ans=0;
while(l<=r)
{
int m=l+r>>1;
if(RMQ(i,m)>=qq) l=m+1,ans=m;
else r=m-1;
}
return ans;
}
int rsearch(int l,int r,int qq,int i)//固定右端点
{
int ans=0;
while(l<=r)
{
int m=l+r>>1;
if(RMQ(m,i)<qq)l=m+1 ;//手抖写成RMQ(i,m)一直没发现,debug两个钟
else r=m-1,ans=m;
}
return ans;
}
void init(int n)//维护lft rft
{
for(int i=1;i<=n;i++)
{
lft[i].clear();
int la=i;
int qq=RMQ(i,i);
while(1)
{
int ans= lsearch(la,n,qq,i);//二分找下一个转折下标ans
lft[i].push_back(node(ans,qq));
if(ans==n)break;
la=ans+1;
qq=RMQ(i,la);
}
rft[i].clear();
la=i;
qq=RMQ(i,i);
while(1)
{
int ans=rsearch(1,la,qq,i);
rft[i].push_back(node(ans,qq));
if(ans==1)break;
la=ans-1;
qq=RMQ(la,i);
}
}
}
bool cmp(mo a,mo b)
{
if(bel[a.ql]==bel[b.ql])return a.qr<b.qr;
return a.ql<b.ql;
}
bool cmp2(mo a,mo b)
{
return a.id<b.id;
}
ll ans=0;
void findL(int l,int r,int ty)//莫队转移
{
ll tem=0;int la=l;
for(int i=0;i<lft[l].size();i++)
{
ll pos=lft[l][i].pos;
ll gcd=lft[l][i].gcd;
tem+=1ll*(min(1ll*r,pos)-la+1)*gcd;
la=pos+1;
if(la>r)break;
}
if(ty==1)ans+=tem;
else ans-=tem;
}
void findR(int l,int r,int ty)//莫队转移
{
ll tem=0;
int la=r;
for(int i=0;i<rft[r].size();i++)
{
ll pos=rft[r][i].pos;ll gcd=rft[r][i].gcd;
tem+=1ll*(la-max(1ll*l,pos)+1)*gcd;
la=pos-1;
if(la<l)break;
}
if(ty==1) ans+=tem;
else ans-=tem;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int unit=(int)sqrt(n);//莫队分块套路
for(int i=1;i<=n;i++)scanf("%d",&a[i]),bel[i]=i/unit+1;
ST(n);
init(n);
int qqq;
scanf("%d",&qqq);
int idx=0;
while(qqq--)
{
idx++;
scanf("%d %d",&q[idx].ql,&q[idx].qr);
q[idx].id=idx;
}
int l=1,r=0;
sort(q+1,q+1+idx,cmp);
ans=0;
for(int i=1;i<=idx;i++)
{
while(r<q[i].qr)r++,findR(l,r,1);
while(r>q[i].qr)findR(l,r,-1),r--;
while(l>q[i].ql)l--,findL(l,r,1);
while(l<q[i].ql)findL(l,r,-1),l++;
res[q[i].id]=ans;
}
for(int i=1;i<=idx;i++) printf("%lld\n",res[i]);
}
}