传送门
就是给你n个数字,m次查询,查询gcd为(a_i)的区间有几个
因为是gcd,并且是区间查询,那么考虑(ST)表进行(O(1))查询,而且,在(ST)表里面,gcd是不递增的序列,那么就可以进行二分查找
那么再去考虑对于每一个左区间,查询它的所有右区间的gcd值,考虑到,前一个gcd值肯定是后一个gcd值2倍以上,因为2是最小的素数,那么gcd值就只有(logn)个,即可以考虑二分查找每一个gcd值的右区间即可。
预处理好答案,存放在map里面,时间复杂度(O(nlog^2n)),然后对于每个查询进行(O(1))查询
有关于gcd区间只查询的题,很多情况考虑ST表,而ST表又是单调的,又可以考虑二分
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}
namespace ST{
int st[N][20];
int lg[N];
int query(int l, int r){ // 查询区间[l,r]的最值
int k = lg[r - l + 1];
return gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
void init(int *a, int n){ // 初始化
for(int i = 1; i <= n; i++) st[i][0] = a[i];
int k = log2(n / 2) + 1;
lg[1] = 0;
for(int i = 2;i <= n; i++) lg[i] = lg[i >> 1] + 1;
for(int j = 1; j <= k; j++)
for(int i = 1; i + (1 << j) - 1 <= N; i++)
st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
};
int n, a[N];
std::map<int, ll> mp;
int binary_search(int l, int r, int d){
int left = l;
for(int i = 1; i <= 100; i++) {
int mid = (l + r) >> 1;
if(ST::query(left, mid) >= d) l = mid + 1;
else r = mid - 1;
}
return (l + r) >> 1;
}
void pre(){
for(int i = 1; i <= n; i++) {
int d = a[i], left = i;
int right = binary_search(i, n, d);
while(right != n){
mp[ST::query(i, right)] += right - left + 1;
left = right + 1;
d = ST::query(i, right + 1);
right = binary_search(i, n, d);
}
mp[ST::query(i, n)] += right - left + 1;
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
ST::init(a, n);
int m; scanf("%d", &m);
pre();
for(int i = 1; i <= m; i++) {
int x; scanf("%d", &x);
printf("%lld
", mp[x]);
}
return 0;
}