题意
给n个数{a[i]},q个询问(l,r),与[l,r]最大公约数相同的区间有多少个。
思路
首先是查询最大公约数,然后统计相同的区间。查询可以用各种结构,既然只用查询那就用st简单高效,主要考虑统计。指定开头s,那么从s开始的区间,随着结尾t的减小,gcd减小,gcd种类不超过log(a[s])个,可以二分每种gcd结束的位置,然后累加。这样全统计时间复杂度为O(n*logn*logn),鉴于gcd的log很小不会超时。内存方面,可以用一个map记录询问了哪些gcd,然后只累加相应的结果,其他的就不管了。
AC代码 C++
#include <stdio.h>
#include <algorithm>
#include <map>
#define MAXN 100005
using namespace std;
int a[MAXN];
int st[MAXN][20];
int ans[MAXN];
inline int rmq(int l, int r)
{
int limit=0;
while(1 << limit+1 <= r - l + 1)
limit++;
return __gcd(st[l][limit], st[r-(1<<limit)+1][limit]);
}
int main()
{
int T, t, n, q, i, j, k, l, r, limit;
scanf("%d", &T);
for(t=1; t<=T; t++)
{
scanf("%d", &n);
for(i=1; i<=n; i++)
scanf("%d", a+i);
for(i=1, limit=0; i<=n; i++)
st[i][0] = a[i];
while(1 << limit <= n)
limit++;
for(i=1; i<limit; i++)
for(j=1; j+(1<<i)<n+2; j++)
st[j][i] = __gcd(st[j][i-1], st[j+(1<<i-1)][i-1]);
map<int, long long> sum;
scanf("%d", &q);
for(i=0; i<q; i++)
{
scanf("%d%d", &l, &r);
sum[ans[i] = rmq(l, r)] = 0;
}
for(i=1; i<=n; i++)
{
j = i;
while(true)
{
limit = rmq(i, j);
l = j;
r = n + 1;
while(l < r)
{
k = l + r >> 1;
if(rmq(i, k) != limit)
r = k;
else
l = k + 1;
}
if(sum.find(limit) != sum.end())
sum[limit] += (long long)r - j;
if(r > n)
break;
else
j = r;
}
}
printf("Case #%d:\n", t);
for(i=0; i<q; i++)
printf("%d %I64d\n", ans[i], sum[ans[i]]);
}
return 0;
}