数据结构_ST算法+二分搜索_GCD问题

来源:互联网 发布:罗伯特劳伦斯库恩知乎 编辑:程序博客网 时间:2024/05/17 08:58

区间GCD问题

区间GCD问题,一般指:给出一序列正整数数,每次询问一个整数K,求GCD等于K的范围有多少个。问题的朴素的求法时间复杂度很高,考虑是否可以用一些方法来优化。

ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

而且,它也可以在相同的时间复杂度下计算区间GCD的值。

对于而且在固定L的情况下,区间GCD的值随R递增呈不上升趋势。我们可以通过二分搜索来寻找分段范围。

例题1 HDU 5726 GCD

#include<cstdio>#include<cmath>#include<algorithm>#include<map>using namespace std;typedef __int64 ll;const int maxn = 100000;int N;map<int, ll> m;int st[maxn + 10][64];void InitST() {    //计算ST    for (int i = 1; (1 << i) <= N; i ++) {        for(int j = 1; j <= N + 1 - (1 << i); j ++) {            st[j][i] = __gcd( st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);        }    }}int AskST(int l, int r) {    int tmp = int(log2(r - l + 1));    return __gcd( st[l][tmp], st[r - (1 << tmp) + 1][tmp]);}void init() {    m.clear();    for(int i = 1; i <= N; i++) {        ll now = st[i][0];        int index = i;        while(index <= N) {            int left = index;            int right = N;            while(left <= right) {                int mid = (left + right) >> 1;                if(AskST(i, mid) >= now)                    left = mid + 1;                else                    right = mid - 1;            }            m[now] += left - index;            now = AskST(i, left);            index = left;        }    }}int main() {    int T, x, y;    scanf("%d", &T);    for(int CASE = 1; CASE <= T; CASE++) {        printf("Case #%d:\n", CASE);        scanf("%d", &N);        for(int i = 1; i <= N; i++) {            scanf("%d", &st[i][0]);        }        int mm;        scanf("%d", &mm);        InitST();        init();        while(mm--) {            scanf("%d%d", &x, &y);            int ans = AskST(x, y);            printf("%d %I64d\n", ans, m[ans]);        }    }}
0 0