HUD 5726 GCD
给一个序列,多次查询区间的最大公约数,并求出同样是这个最大公约数的区间有多少个。
区间查询采用ST表,第二问查询利用区间向右延伸最大公约数递减的规律可通过二分快速找到右边界。把第一问的答案先求出来,表示要查询的公约数加入map。
然后枚举左端点二分右端点,把已经从存在map的值加上找到的区间数,不存在的就不用管了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<list>
#include<map>
#include<math.h>
//#include<Windows.h>
using namespace std;
#define min(a,b) (a < b ? a:b)
#define max(a,b) (a > b ? a:b)
#define LL long long int
int T, N, Q;
int dp[100005][17];
int qurey[100005][3];
map<int, LL> m;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int GCD(int l, int r)
{
int flr = log2(r - l + 1), len = (1 << flr) ;
return gcd(dp[l][flr], dp[r - len + 1][flr]);
}
int main()
{
int Case = 0;
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
scanf("%d", &T);
while(T--)
{
++Case;
m.clear();
scanf("%d", &N);
for(int i = 0; i < N; ++i)
{
scanf("%d", &dp[i][0]);
}
for(int t = 1; (1 << t) <= N; ++t)
{
for(int i = 0; i + (1 << t) <= N; ++i)
{
dp[i][t] = gcd(dp[i][t - 1], dp[i + (1 << (t - 1))][t - 1]);
}
}
scanf("%d", &Q);
printf("Case #%d:\n", Case);
int l, r;
for(int i = 0; i < Q; ++i)
{
scanf("%d %d", &qurey[i][0], &qurey[i][1]);
qurey[i][2] = GCD(qurey[i][0] - 1, qurey[i][1] - 1);
m[qurey[i][2]] = 0;
}
for(int i = 0; i < N; ++i)
{
int j = i;
while(j < N)
{
int d = GCD(i, j);
int l = j, r = N - 1;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(GCD(i, mid) < d)
{
r = mid - 1;
}
else
{
l = mid;
}
}
if(m.find(d) != m.end())
{
m[d] += (l - j + 1);
}
j = r + 1;
}
}
for(int i = 0; i < Q; ++i)
{
printf("%d %lld\n", qurey[i][2], m[qurey[i][2]]);
}
}
//system("pause");
}