hdu 5726 GCD
题意:
给出一个数组,每次询问区间[l,r]的gcd是多少,并且这个数组有多少个连续的区间的gcd和[l,r]的gcd相等。
思路:
区间询问RMQ问题,可以用st表解决,预处理的时间是O(nlogn),一次查询的时间是O(logn)。
关键是第二个问题,如何找出这些区间的数量。
如果固定区间的左端点,那么随着区间长度的增大,gcd肯定是非递增的,减少的话一定是每次至少减少1/2,所以一个区间内的gcd最多有log(1e9)个。
所以固定左端点,二分当前的gcd能达到的最远的右端点的位置,然后加到一个map当中进行维护,这也是一个预处理,那么每次就可以o(logn)进行回答。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <math.h> 5 #include <map> 6 using namespace std; 7 typedef long long ll; 8 const int N = 1e5 + 10; 9 int a[N],dp[N][32]; 10 map<int,ll> mmp; 11 int gcd(int x,int y) 12 { 13 return y ? gcd(y,x%y) : x; 14 } 15 void init(int n) 16 { 17 mmp.clear(); 18 for (int i = 0;i < n;i++) 19 { 20 dp[i][0] = a[i]; 21 } 22 int bitn = (int)(log(n)/log(2.0)); 23 for (int j = 1;j <= bitn;j++) 24 { 25 for (int i = 0;i < n;i++) 26 { 27 if (i + (1 << j) - 1 >= n) break; 28 dp[i][j] = gcd(dp[i][j-1],dp[i+(1 << (j-1))][j-1]); 29 } 30 } 31 } 32 int que(int l,int r) 33 { 34 int k = (int)(log(r-l+1.0) / log(2.0)); 35 return gcd(dp[l][k],dp[r-(1<<k)+1][k]); 36 } 37 int main() 38 { 39 int T; 40 scanf("%d",&T); 41 int kase = 0; 42 while (T--) 43 { 44 int n; 45 scanf("%d",&n); 46 for (int i = 0;i < n;i++) scanf("%d",&a[i]); 47 printf("Case #%d:\n",++kase); 48 init(n); 49 for (int i = 0;i < n;i++) 50 { 51 int j = i; 52 while (j < n) 53 { 54 int tmp = que(i,j); 55 int l = j,r = n; 56 while (r - l > 1) 57 { 58 int mid = (l + r) >> 1; 59 if (que(i,mid) == tmp) l = mid; 60 else r = mid; 61 } 62 mmp[tmp] += l - j + 1; 63 j = l + 1; 64 } 65 } 66 int q; 67 scanf("%d",&q); 68 while (q--) 69 { 70 int l,r; 71 scanf("%d%d",&l,&r); 72 l--,r--; 73 int ans = que(l,r); 74 printf("%d %lld\n",ans,mmp[ans]); 75 } 76 } 77 return 0; 78 }
康复训练中~欢迎交流!