GCD (ST表,二分求區間查詢)
阿新 • • 發佈:2019-01-07
HUD 5726 GCD
給一個序列,多次查詢區間的最大公約數,並求出同樣是這個最大公約數的區間有多少個。
區間查詢採用ST表,第二問查詢利用區間向右延伸最大公約數遞減的規律可通過二分快速找到右邊界。把第一問的答案先求出來,表示要查詢的公約數加入map。
然後列舉左端點二分右端點,把已經從存在map的值加上找到的區間數,不存在的就不用管了。
#include<stdio.h> #include<string.h> #include<algorithm> #include<list> #include<map> #include<math.h> //#include<Windows.h> using namespace std; #define min(a,b) (a < b ? a:b) #define max(a,b) (a > b ? a:b) #define LL long long int int T, N, Q; int dp[100005][17]; int qurey[100005][3]; map<int, LL> m; int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } int GCD(int l, int r) { int flr = log2(r - l + 1), len = (1 << flr) ; return gcd(dp[l][flr], dp[r - len + 1][flr]); } int main() { int Case = 0; //freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); scanf("%d", &T); while(T--) { ++Case; m.clear(); scanf("%d", &N); for(int i = 0; i < N; ++i) { scanf("%d", &dp[i][0]); } for(int t = 1; (1 << t) <= N; ++t) { for(int i = 0; i + (1 << t) <= N; ++i) { dp[i][t] = gcd(dp[i][t - 1], dp[i + (1 << (t - 1))][t - 1]); } } scanf("%d", &Q); printf("Case #%d:\n", Case); int l, r; for(int i = 0; i < Q; ++i) { scanf("%d %d", &qurey[i][0], &qurey[i][1]); qurey[i][2] = GCD(qurey[i][0] - 1, qurey[i][1] - 1); m[qurey[i][2]] = 0; } for(int i = 0; i < N; ++i) { int j = i; while(j < N) { int d = GCD(i, j); int l = j, r = N - 1; while(l < r) { int mid = (l + r + 1) >> 1; if(GCD(i, mid) < d) { r = mid - 1; } else { l = mid; } } if(m.find(d) != m.end()) { m[d] += (l - j + 1); } j = r + 1; } } for(int i = 0; i < Q; ++i) { printf("%d %lld\n", qurey[i][2], m[qurey[i][2]]); } } //system("pause"); }