HDU 5726 GCD 【st表+二分+gcd+离线】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hhhhhhxh/article/details/81569310

题目链接

题意:给n个数和q次查询,每次查询输入 [ l , r ] ,输出在序列中有多少个子区间的gcd值与查询的这个区间相同。

大致可以将题目的主要矛盾分为2点:
1. 快速的查询区间gcd值
2. 由于不能遍历所有子区间,如何尽可能的减少遍历的子区间

解决方法:

1.因为只需要查询,可以简单的用st表实现。

for (int j = 1; j <= 20; j++) {
    for (int i = 1; i + (1 << j) - 1 <= n; i++) {
        st[j][i] = __gcd(st[j - 1][i], st[j - 1][i + (1 << j - 1)]);
    }
}
int query(int l, int r) {
    int d = Log[r - l + 1];
    return __gcd(st[d][l], st[d][r - (1 << d) + 1]);
}

2.考虑到gcd具有单调性,即固定左端点,随着右端点慢慢往右,gcd的值只会越来越小。可以通过二分找出就目前的gcd来说,右端点最远的位置。

通过这种方法可以找出所有的gcd,但是由于空间的原因,不能把所有的gcd都存下来。我们只需要记录需要查询的gcd出现的次数,用map来维护,不需要查询的就不管它。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;

int T, n, q, st[25][maxn], Log[maxn], l[maxn], r[maxn], kase = 0;

int query(int l, int r) {
    int d = Log[r - l + 1];
    return __gcd(st[d][l], st[d][r - (1 << d) + 1]);
}

map <int, ll> mp;

int main() {
#ifdef __APPLE__
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    Log[2] = 1;
    for (int i = 3; i <= 100001; i++)   Log[i] = Log[i >> 1] + 1;
    cin >> T;
    while (T--) {
        mp.clear();
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> st[0][i];
        }
        for (int j = 1; j <= 20; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                st[j][i] = __gcd(st[j - 1][i], st[j - 1][i + (1 << j - 1)]);
            }
        }
        cin >> q;
        for (int i = 1; i <= q; i++) {
            cin >> l[i] >> r[i];
            mp[query(l[i], r[i])] = 0;
        }
        for (int i = 1; i <= n; i++) {
            int j = i;
            while (1) {
                int gcd = query(i, j);
                int left = j, right = n;
                int x = 0;
                while (left <= right) {
                    int mid = (left + right) >> 1;
                    if (query(i, mid) == gcd) {
                        x = mid;
                        left = mid + 1;
                    }
                    else {
                        right = mid - 1;
                    }
                }
                if (mp.find(gcd) != mp.end()) {
                    mp[gcd] += x - j + 1;
                }
                j = x + 1;
                if (j > n)
                    break;
            }
        }
        cout << "Case #" << ++kase << ":" << endl;
        for (int i = 1; i <= q; i++) {
            int gcd = query(l[i], r[i]);
            cout << gcd << " " << mp[gcd] << endl;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/hhhhhhxh/article/details/81569310