[关闭]
@Acqua 2019-03-08T12:30:58.000000Z 字数 1337 阅读 1003

HDU5726 GCD(March. 2nd, 2019) 二分查找 特殊技巧

ST表

题目来源

题意

给定一个长度为的序列和个询问,每个询问要求回答区间内元素的最大公因数,并求出有多少个区间的最大公因数与区间相同。

原理

最大公因数问题具有可重性,所以可以使用表。
根据套路计算出区间的最大公因数。
枚举区间左端点,二分查找右端点,具体流程见注释。

反思

  1. 积累特殊技巧,直到可以自己设计出特殊技巧。

代码

  1. // La Pluie
  2. #include<map>
  3. #include<cstdio>
  4. #include<cstring>
  5. #include<iostream>
  6. using namespace std;
  7. typedef long long ln;
  8. const int N = 1e5 + 5;
  9. int dp[N][21], log[N];
  10. map <int, ln> v;
  11. inline int gcd(int x, int y){
  12. return y ? gcd(y, x % y) : x;
  13. }
  14. inline int st(int l, int r){
  15. int d = log[r - l + 1];
  16. return gcd(dp[l][d], dp[r - (1 << d) + 1][d]);
  17. }
  18. int main(){
  19. int T, n, q, cnt = 0;
  20. scanf("%d", &T);
  21. while(T--){
  22. v. clear();
  23. scanf("%d", &n); log[0] = -1;
  24. for(int i = 1; i <= n; i++)
  25. scanf("%d", &dp[i][0]), log[i] = log[i >> 1] + 1;
  26. for(int j = 1; j <= log[n]; j++)
  27. for(int i = 1; i <= n; i++)
  28. if(i + (1 << j - 1) <= n) dp[i][j] = gcd(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
  29. for(int i = 1; i <= n; i++){
  30. int L = i, R = i; // 区间[L,R]为扩展区间,即在原区间[i,L]外扩展出的区间。初始为[i,i]。
  31. while(R <= n){
  32. int l = L, r = n, g = st(i, L); // 由于最大公因数随数字的变多而只减不增,并且最大公因数相同段被尽量长地统计,所以如果把所有g输出来,必呈降序。
  33. while(l <= r){
  34. int mid = (l + r) >> 1;
  35. if(st(i, mid) == g) l = mid + 1;
  36. else r = mid - 1;
  37. }
  38. R = l;
  39. v[g] += (ln)R - L; // 增加的区间分别为[i,L],[i,L+1],[i,L+2],……,[i,R]。此处的R实际上是下一个扩展区间的起点,并且v[g]加上的应该是: 真正的R - L + 1。由于下一扩展区间的起点是 真正的R + 1,所以此处就代换掉了。
  40. L = R;
  41. }
  42. }
  43. printf("Case #%d:\n", ++cnt);
  44. scanf("%d", &q);
  45. while(q--){
  46. int l, r;
  47. scanf("%d %d", &l, &r);
  48. int g = st(l, r);
  49. printf("%d %lld\n", g, v[g]);
  50. }
  51. }
  52. return 0;
  53. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注