题意:
n
(n<105)
个正整数,q
(q<105)
次查询,每次给出l、r,输出区间[l, r]的gcd和与其gcd相同的区间个数。
思路:
rmq预处理gcd,O(nlogn),每次查询O(1);
固定l,gcd随r增大而递减,并且每次最小/2,个数不会超过log1e9,所以可以用二分log时间内求出所有gcd个数,枚举l,map保存,复杂度O(n*logn*logn);
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
const int MAXN = 1e6;
int a[MAXN], st[MAXN][20];
map<int, long long> s;
int gcd(int a, int b)
{
if(!b)
return a;
return gcd(b, a%b);
}
void init(int n)
{
for(int i = 1; i <= n; i++)
st[i][0] = a[i];
int k = log2(n);
for(int j = 1; j <= k; j++)
for(int i = 1; i <= n; i++)
st[i][j] = gcd(st[i][j-1], st[i+(1<<j-1)][j-1]);
}
int rmq(int l, int r)
{
int k = log2(r - l + 1);
return gcd(st[l][k], st[r-(1<<k)+1][k]);
}
void count(int n)
{
s.clear();
for(int i = 1; i <= n; i++)
{
int l = i;
while(l <= n)
{
int pos = l;
int r = n;
int g = rmq(i, l);
while(l < r)
{
int mid = l + r + 1 >> 1;
if(rmq(i, mid) >= g) l = mid ;
else r = mid - 1;
}
s[g] += l - pos + 1;
l++;
}
}
}
int main(int argc, char const *argv[])
{
int t;
scanf("%d", &t);
for(int k = 1; k <= t; k++)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int m;
scanf("%d", &m);
printf("Case #%d:\n", k);
init(n);
count(n);
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
int ans = rmq(l, r);
printf("%d %lld\n", ans, s[ans]);
}
}
return 0;
}