The sum of gcd
Problem Description
You have an array A,the length of
A
A
A is
n
n
n
Let
f
(
l
,
r
)
=
∑
i
=
l
r
∑
j
=
i
j
g
c
d
(
a
i
,
a
i
+
1
,
.
.
.
,
a
j
)
f(l,r)=\sum_{i=l}^r\sum_{j=i}^jgcd(a_i,a_{i+1},...,a_j)
f(l,r)=∑i=lr∑j=ijgcd(ai,ai+1,...,aj)
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≤T≤3
1≤T≤3
1
≤
n
,
Q
≤
1
0
4
1≤n,Q≤10^4
1≤n,Q≤104
1
≤
a
i
≤
1
0
9
1≤a_i≤10^9
1≤ai≤109
1
≤
l
<
r
≤
n
1≤l<r≤n
1≤l<r≤n
Output
For each question,you need to print f ( l , r ) f(l,r) 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
题意
有序列 A A A长度为 n n n,对于每个询问,求 f ( l , r ) = ∑ i = l r ∑ j = i r g c d ( a i , a i + 1 , . . . , a j ) f(l,r)=\sum_{i=l}^r\sum_{j=i}^rgcd(a_i,a_{i+1},...,a_j) f(l,r)=∑i=lr∑j=irgcd(ai,ai+1,...,aj)。
思路:
区间
[
l
,
r
]
[l,r]
[l,r]内所有子区间的gcd 的和。
离线处理。所有询问以r为主键排列。从1到n依次插入
a
i
a_i
ai,对于
1
≤
j
≤
i
1\le j\le i
1≤j≤i,求出区间
[
j
,
i
]
[j,i]
[j,i]的gcd,并将其加到j上。当r == i时,求出
[
l
,
r
]
[l,r]
[l,r]所维护的信息的和。
信息维护:考虑线段树维护信息。在插入 a i a_i ai时,对于 1 ≤ j ≤ i 1\le j\le i 1≤j≤i,区间gcd的值最多有 l o g 2 i log_2 i log2i个不同的值,且相同值之间是连续的。可以维护线性表,求出每个不同的区间gcd的值。 j ∈ [ a l , a r ] j\in[al,ar] j∈[al,ar]时,区间 [ j , i ] [j,i] [j,i]的gcd为x,线段树维护区间更新。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-6
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 10010;
const int mod = 1000000007;
struct node{
int l, r, id;
bool operator<(node b)const{
return r<b.r;
}
}p[maxn];
int n, a[maxn];
LL ans[maxn], b[maxn*4], lz[maxn*4];
vector<P> g1, g2;
int gcd(int a, int b);
void solve(int pos, int x);
void pushdown(int k, int l, int r);
LL query(int l, int r, int al, int ar, int k);
void Update(int l, int r, int al, int ar, int y, int k);
int main()
{
int t, m, i, j, k, q;
scanf("%d", &t);
while(t--)
{
g1.clear();
g2.clear();
memset(b, 0, sizeof(b));
memset(lz, 0, sizeof(lz));
scanf("%d", &n);
for(i=1;i<=n;i++)
scanf("%d", &a[i]);
scanf("%d", &q);
for(i=0;i<q;i++){
scanf("%d %d", &p[i].l, &p[i].r);
p[i].id = i;
}
sort(p, p+q);
j = 0;
for(i=1;i<=n;i++){
solve(i, a[i]);
while(j<q && p[j].r == i){
ans[p[j].id] = query(1, n, p[j].l, i, 1);
j++;
}
}
for(i=0;i<q;i++)
printf("%lld\n", ans[i]);
}
return 0;
}
void solve(int pos, int x)
{
g2.clear();
int l, r, i, j, k;
i = g1.size()-1, l = pos, r = pos+1;
do{
while(i>=0 && gcd(x,g1[i].second) == x)
l=g1[i].first, i--;
Update(1, n, l, r-1, x, 1);
g2.push_back(P(l, x));
r = l;
//printf("l:%d x:%d\n", l, x);
if(i>=0)x = gcd(x, g1[i].second);
}while(i>=0);
g1.clear();
for(i=g2.size()-1;i>=0;i--)
g1.push_back(g2[i]);
}
int gcd(int a, int b)
{
return b==0?a:gcd(b, a%b);
}
void Update(int l, int r, int al, int ar, int y, int k)
{
if(l == al &&r == ar){
b[k] += (r-l+1LL)*y;
lz[k] += y;
return ;
}
int mid = (l+r)/2;
pushdown(k, mid-l+1, r-mid);
if(ar <= mid)Update(l, mid, al, ar, y, 2*k);
else if(al > mid)Update(mid+1, r, al, ar, y, 2*k+1);
else Update(l, mid, al, mid, y, 2*k),
Update(mid+1, r, mid+1, ar, y, 2*k+1);
b[k] = b[2*k]+b[2*k+1];
}
void pushdown(int k, int l, int r)
{
if(lz[k])
{
lz[2*k] += lz[k];
lz[2*k+1] += lz[k];
b[2*k] += lz[k]*l;
b[2*k+1] += lz[k]*r;
lz[k] = 0;
}
}
LL query(int l, int r, int al, int ar, int k)
{
if(l == al && r == ar)return b[k];
int mid = (l+r)/2;
pushdown(k, mid-l+1, r-mid);
if(ar<=mid)return query(l, mid, al, ar, 2*k);
else if(al>mid)return query(mid+1, r, al, ar, 2*k+1);
else return query(l, mid, al, mid, 2*k)+
query(mid+1, r, mid+1, ar, 2*k+1);
}