题意:
给出一个长度为N的序列。Q次询问,每次询问一段区间的gcd值以及有多少区间的gcd值等于该值。
题解:
预处理st表。枚举左边界二分右边界求出所有gcd值的情况数。
#include <iostream> #include <cstdio> #include <cmath> #include <map> using namespace std; typedef long long ll; const int maxn = 1e5+10; int t; int n, q; int dp[maxn][20]; int ql[maxn], qr[maxn]; map<int, ll> m; int gcd(int x, int y) { return y==0?x:gcd(y, x%y); } int GCD(int l, int r) { int x = log2(r-l+1); return gcd(dp[l][x], dp[r-(1<<x)+1][x]); } int main() { scanf("%d", &t); for(int casee = 1; casee <= t; casee++) { m.clear(); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &dp[i][0]); for(int j = 1; (1<<j) <= n; j++) for(int i = 1; i+(1<<j)-1 <= n; i++) dp[i][j] = gcd(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); scanf("%d", &q); for(int i = 1; i <= q; i++) { scanf("%d%d", &ql[i], &qr[i]); int gg = GCD(ql[i], qr[i]); m[gg] = 0; } for(int i = 1; i <= n; i++) { int r = n; while(r >= i) { int l = i, nr = r; int now = GCD(l, r); while(l <= r) { int mid = l+r>>1; if(GCD(i, mid) > now) l = mid+1; else r = mid-1; } if(m.find(now) != m.end()) m[now] += nr-r; } } printf("Case #%d: ", casee); for(int i = 1; i <= q; i++) { int gg = GCD(ql[i], qr[i]); printf("%d %lld ", gg, m[gg]); } } }