思路:将数据存入gcd二维数组的第0列,第j列的gcd值是第j-1列的i行和i+2的j次方的最大公约数,
gcd[i][j] = Gcd(gcd[i][j - 1], gcd[i + bin[j - 1]][j - 1]);
将gcd数组计算好后,进行询问query,由于系统的Log2太慢,所以编写了Log数组直接使用。
int k = Log[r - l + 1];
return Gcd(gcd[l][k], gcd[r - bin[k] + 1][k]);
二分查找bi函数:将左端固定值为x,寻找值仍为x的右端,并返回;遍历完成即统计完成。
1 #include<iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include<algorithm> 5 #include<map> 6 #define maxn 100010 7 using namespace std; 8 typedef long long LL; 9 int gcd[maxn][32], n, Log[maxn], bin[32]; 10 map<int, LL> mp; 11 int Gcd(int a, int b) { 12 return b ? Gcd(b, a % b) : a; 13 } 14 void rmqInit() { 15 Log[0] = -1; bin[0] = 1; 16 for (int i = 1; i < 20; ++i) { 17 bin[i] = bin[i - 1] << 1;//1 2 4 8 16 32 18 } 19 for (int i = 1; i <= n; ++i) { 20 Log[i] = Log[i >> 1] + 1;//-1 0 1 1 2 2 2 2 3 3 3 3 3 3 3 3 21 } 22 for (int j = 1; bin[j] <= n; ++j) { 23 for (int i = 1; i + bin[j - 1] - 1 <= n; ++i) { 24 gcd[i][j] = Gcd(gcd[i][j - 1], gcd[i + bin[j - 1]][j - 1]); 25 } 26 } 27 } 28 int query(int l, int r) {//询问区间为l,r的gcd值 29 int k = Log[r - l + 1];// 30 return Gcd(gcd[l][k], gcd[r - bin[k] + 1][k]);//bin[k]就是2的k次方 31 } 32 int bi(int i, int l, int r, int x) { //x是i到l的最大公约数 33 while (r - l > 1) { //右区间-左区间等于1时停止 34 int mid = l + r >> 1; 35 int val = query(i, mid); 36 if (val >= x) l = mid; 37 else r = mid - 1; 38 } 39 //找出最大公约数是x的右区间 40 return query(i, r) == x ? r : l; 41 } 42 int main() { 43 scanf_s("%d", &n); 44 for (int i = 1; i <= n; ++i) { 45 cin >> gcd[i][0]; 46 } 47 rmqInit(); 48 for (int i = 1; i <= n; ++i) { 49 int l = i; //左区间从1到n,一行一行搜索 50 while (true) { 51 if (l == n + 1) break; //左区间超过n则跳出 52 int val = query(i, l); //询问区间为i,l的gcd值 53 int r = bi(i, l, n, val); 54 mp[val] += r - l + 1; 55 l = r + 1; 56 } 57 } 58 int q, x; 59 scanf_s("%d", &q); 60 while (q--) { 61 scanf_s("%d", &x); 62 printf("%I64d ", mp[x]); 63 } 64 return 0; 65 }