[HDOJ5726]GCD(RMQ,二分)

时间:2023-03-09 20:05:53
[HDOJ5726]GCD(RMQ,二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726

题意:给定数列,求区间[L,R]的GCD的值,并求出有多少个子区间满足和[L,R]的GCD相等。

RMQ预处理所有区间的GCD,枚举所有区间的左边界i,起初固定右边界j,二分枚举右边界j的最大值,使得[i,j]区间内的GCD不变,更新对应GCD的值,大小为j-i。

 #include <bits/stdc++.h>
using namespace std; typedef long long LL;
const int maxn = ;
int n, q;
LL a[maxn];
LL dp[maxn][];
map<LL, LL> cnt; LL gcd(LL x, LL y) {
return y == ? x : gcd(y, x%y);
} void st() {
for(int i = ; i <= n; i++) dp[i][] = a[i];
for(int j = ; ( << j) <= n; j++) {
for(int i = ; i + ( << j) - <= n; i++) {
dp[i][j] = gcd(dp[i][j-], dp[i+(<<(j-))][j-]);
}
}
} LL query(int l, int r) {
int j = ;
while( << (j + ) <= r - l + ) j++;
return gcd(dp[l][j], dp[r-(<<j)+][j]);
} inline bool scan_d(LL &num) {
char in;bool IsN=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&(in<''||in>'')) in=getchar();
if(in=='-'){ IsN=true;num=;}
else num=in-'';
while(in=getchar(),in>=''&&in<=''){
num*=,num+=in-'';
}
if(IsN) num=-num;
return true;
} int main() {
//freopen("in", "r", stdin);
int T, _ = ;
scanf("%d", &T);
int l, r;
while(T--) {
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scan_d(a[i]);
}
st(); cnt.clear();
for(int i = ; i <= n; i++) {
int j = i;
while(j <= n) {
LL cur = query(i, j);
int lo = j, hi = n;
while(lo <= hi) {
int mid = (lo + hi) >> ;
if(query(i, mid) >= cur) lo = mid + ;
else hi = mid - ;
}
cnt[cur] += (LL)(hi - j + );
j = hi + ;
}
}
printf("Case #%d:\n", _++);
scanf("%d", &q);
while(q--) {
scanf("%d %d", &l, &r);
LL ret = query(l, r);
cout << ret << " " << cnt[ret] << endl;
}
}
return ;
}