The sum of gcd
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description
You have an array
A
,the length of
A
is
n
Let f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
Let f(l,r)=∑ri=l∑rj=igcd(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≤n,Q≤104
1≤ai≤109
1≤l<r≤n
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≤104
1≤ai≤109
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
上一篇是用gcd性质处理出每个端点的gcd变化位置,这一篇是用了RMQ加二分预处理出所有的gcd变化位置,然后再用莫队算法求解。
类似的RMQ二分预处理还有
HDU 5726这一题。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <bitset>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define maxn 50500
using namespace std;
typedef long long ll;
const int N = 10000 + 5;
int n, m;
int block;
int a[N];
ll ans[N];
int ggcd[23][maxn*2];
struct Node
{
int L, R;
int id;
bool operator<(const Node&rhs)const
{
if (L / block == rhs.L / block)
{
return R < rhs.R;
}
return L / block < rhs.L / block;
}
} q[N];
int gcd(int a, int b)
{
return (b == 0) ? a : gcd(b, a % b);
}
void ST(int num)
{
for (int i = 1; i <= num; i++)
{
ggcd[0][i] = a[i];
}
for (int i = 1; i <= log2(num); i++)
for (int j = 1; j <= num; j++)
if (j + (1 << i) - 1 <= num)
{
int a = ggcd[i - 1][j], b = ggcd[i - 1][j + (1 << i >> 1)];
ggcd[i][j]=gcd(a,b);
}
}
int RMQ(int x, int y)
{
int k = (int) log2(y - x + 1.0);
int a = ggcd[k][x], b = ggcd[k][y - (1 << k) + 1];
return gcd(a,b);
}
struct He
{
int idx;
ll g;
};
vector<He>vl[N], vr[N];
void preDeal()
{
ST(n);
for(int i=1;i<=n;i++)
{
int g=ggcd[0][i],j=i;
while(j<=n)
{
int left = j,right=n,mid;
while(left <= right)
{
mid =(left + right)/2;
if(RMQ(i,mid)>=g)
{
left =mid +1;
}
else
{
right =mid -1;
}
}
mid = (left+right)/2;
vr[i].push_back(He{mid,g});
j=mid+1;
g=RMQ(i,j);
}
}
for(int i=n;i>=1;i--)
{
int g=ggcd[0][i],j=i;
while(j>=1)
{
int left = 1,right=j,mid;
while(left <= right)
{
mid =(left + right)/2;
if(RMQ(mid,i)<g)
{
left =mid +1;
}
else
{
right =mid -1;
}
}
mid = (left+right)/2;
vl[i].push_back(He{mid+1,g});
j=mid;
g=RMQ(j,i);
}
}
}
ll calc(int type, int L, int R) //计算区间[L,R]的结果
{
ll res = 0;
if (!type)
{
int tr = R; //当前的右端点
for (auto&it : vl[R])
if (it.idx >= L) //如果当前区间的左端点>=L,则当前区间为[it.idx,tr]
{
//这里其实用到了GCD值的传递性:
//如果L<L1<R,gcd([L,R])=g1,gcd([L1,R])=g2(g2≥g1),那么必有gcd([L,L1-1])=g1
res += (tr - it.idx + 1) * it.g;
tr = it.idx - 1; //更新右端点
}
else //如果当前区间的左端点<L,则应该被算入的区间为[L,tr],由于它是最后一个区间了,因此break
{
res += (tr - L + 1) * it.g;
break;
}
}
else //原理同上
{
int tl = L;
for (auto&it : vr[L])
if (it.idx <= R)
{
res += (it.idx - tl + 1) * it.g;
tl = it.idx + 1;
}
else
{
res += (R - tl + 1) * it.g;
break;
}
}
return res;
}
void solve()
{
for (int i = 1; i <= n; i++)
{
vl[i].clear(), vr[i].clear();
}
block = sqrt(n); //块数
sort(q, q + m);
preDeal();
int L = 1, R = 0;//从(1, 0)点开始转移,此时res = 0
ll res = 0;
for (int i = 0; i < m; i++)
{
while (R < q[i].R)
{
R++;//向右右端点,然后计算[L,R]区间的结果,累加给res
res += calc(0, L, R);
}
while (R > q[i].R) //向左移动右端点
{
res -= calc(0, L, R);
R--;
}
while (L > q[i].L)
{
L--;
res += calc(1, L, R);
}
while (L < q[i].L)
{
res -= calc(1, L, R);
L++;
}
ans[q[i].id] = res; //转移完毕。注意,res,L,R都不要清零
}
for (int i = 0; i < m; i++)
{
printf("%I64d\n", ans[i]);
}
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
scanf("%d", &m);
for (int i = 0; i < m; i++)
{
scanf("%d%d", &q[i].L, &q[i].R);
q[i].id = i;
}
solve();
}
return 0;
}