区间gcd问题 HDU 5869 离线+树状数组

题目大意:长度n的序列, m个询问区间[L, R], 问区间内的所有子段的不同GCD值有多少种. 子段就是表示是要连续的a[]

思路:固定右端点,预处理出所有的gcd,每次都和i-1的gcd比较,然后不断放入gcd即可。

然后就是树状数组的更新,枚举右端点即可。然后我们知道,大区间不如小区间来的实惠,所以我们每次有重复gcd出现的时候,都要把大区间更换成小区间即可

下午脑子有点不清楚。。。2333看别人的博客蒙了好久

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
/*
先总体说一下思路:求出区间的gcd,再对右进行排序,再枚举右端即可
*/
const int maxn = 1e5 + 5;
int tree[maxn], a[maxn], pre[maxn * 10], ans[maxn];
int n, q;
vector<pair<int, int> >qq[maxn], v[maxn];

int lowbit(int x) {return x & -x;}
int gcd(int a, int b){
    return b == 0 ? a : gcd(b, a % b);
}

void update(int x, int val){
    for (int i = x; i <= n; i += lowbit(i)){
        tree[i] += val;
    }
}

int sum(int x){
    int ans = 0;
    for (int i = x; i > 0; i -= lowbit(i)) ans += tree[i];
    return ans;
}

void solve(){
    memset(pre, 0, sizeof(pre));
    memset(tree, 0, sizeof(tree));

    for (int i = 1; i <= n; i++){
        int len = v[i].size();
        for (int j = 0; j < len; j++){
            pair<int, int> g = v[i][j];
            if (pre[g.first]) update(pre[g.first], -1);
            pre[g.first] = g.second;
            update(g.second, 1);
        }
        len = qq[i].size();
        for (int j = 0; j < len; j++){
            pair<int, int> p = qq[i][j];
            ans[p.second] = sum(i) - sum(p.first - 1);
        }
    }
    for (int i = 1; i <= q; i++){
        printf("%d
", ans[i]);
    }
}

int main(){
    while (scanf("%d%d", &n, &q) == 2){
        for (int i = 1; i <= n; i++){
            scanf("%d", a + i);
            v[i].clear();
            qq[i].clear();
        }
        for (int i = 1; i <= n; i++){
            int len = v[i - 1].size();
            int val = a[i], pos = i;
            for (int j = 0; j < len; j++){
                pair<int, int> p = v[i - 1][j];
                int g = gcd(p.first, val);
                if (g != val) {
                    v[i].pb(mk(val, pos));///要优先知道那一个区间里面的gcd
                    val = g;
                    pos = p.second;
                }
            }
            v[i].pb(mk(val, pos));
        }
/*
        for (int i = 1; i <= n; i++){
            int len = v[i].size();
            for (int j = 0; j < len; j++){
                printf("%d&%d   ", v[i][j].first, v[i][j].second);
            }
            printf("
");
        }
*/
        for (int i = 1; i <= q; i++){
            int l, r; scanf("%d%d", &l, &r);
            qq[r].pb(mk(l, i));
        }
        solve();
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5865036.html