zoukankan      html  css  js  c++  java
  • HH的项链 离线/在线 树状/线段树/主席树/莫队

    题目

    HH 有一串由各种漂亮的贝壳组成的项链。

    HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。

    HH 不断地收集新的贝壳,因此他的项链变得越来越长。

    有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?

    这个问题很难回答,因为项链实在是太长了。

    于是,他只好求助睿智的你,来解决这个问题。

    输入格式
    第一行:一个整数 N,表示项链的长度。

    第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为 0 到 1000000 之间的整数)。

    第三行:一个整数 M,表示 HH 询问的个数。

    接下来 M 行:每行两个整数,L 和 R,表示询问的区间。

    输出格式

    M 行,每行一个整数,依次表示询问对应的答案。

    数据范围

    1≤N≤50000,
    1≤M≤2×105,
    1≤L≤R≤N
    

    输入样例:

    6
    1 2 3 4 3 5
    3
    1 2
    3 5
    2 6
    

    输出样例:

    2
    2
    4
    

    题解

    离线算法

    树状数组

    这俩差不多, 树状更好写,

    定住右区间 r 从1~n, 然后维护离 当前 r最近且没有出现过数字的位置

    const int N = 5e4 + 5, M = 1e6 + 5;
    
    int n, m, _, k;
    int a[N], c[N], ls[M], ans[200005];
    pair<PII, int> q[200005];
    
    void add(int x, int k) {
        for (; x <= n; x += -x & x) c[x] += k;
    }
    
    int ask(int x) {
        int ans = 0;
        for (; x; x -= -x & x) ans += c[x];
        return ans;
    }
    
    bool cmp(const pair<PII, int>& a, const pair<PII, int>& b) {
        return a.fi.se < b.fi.se;
    }
    
    int main() {
        IOS; cin >> n;
        rep(i, 1, n) cin >> a[i];
        cin >> m;
        rep(i, 1, m) cin >> q[i].fi.fi >> q[i].fi.se, q[i].se = i;
        
        sort(q + 1, q + 1 + m, cmp);
        for (int i = 1, j = 1; i <= m; ++i) {
            for (; j <= q[i].fi.se; ++j) {
                if (ls[a[j]]) add(ls[a[j]], -1);
                add(j, 1); ls[a[j]] = j;
            }
            ans[q[i].se] = ask(q[i].fi.se) - ask(q[i].fi.fi - 1);
        }
        rep(i, 1, m) cout << ans[i] << '
    ';
        return 0;
    }
    

    线段树

    和树状一样

    线段树就不用我敲了吧? 这道题是单点修改, 直接push_down 不需要lazy标记, 算了敲敲吧~

    struct BIT {
        struct node {
            int val, l, r;
        } tr[N << 2];
    
        void push_up(int rt) {
            tr[rt].val = tr[rt << 1 | 1].val + tr[rt << 1].val;
        }
    
        void build(int rt, int l, int r) {
            tr[rt] = { 0, l, r };
            if (l == r) return;
            int mid = l + r >> 1;
            build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r);
        }
    
        void change(int rt, int x, int k) {
            if (tr[rt].l == x && tr[rt].r == x) { tr[rt].val += k; return; }
            int mid = tr[rt].l + tr[rt].r >> 1;
            if (x <= mid) change(rt << 1, x, k);
            else change(rt << 1 | 1, x, k);
            push_up(rt);
        }
    
        int ask(int rt, int l, int r) {
            if (tr[rt].l >= l && tr[rt].r <= r) return tr[rt].val;
            int ans = 0, mid = tr[rt].l + tr[rt].r >> 1;
            if (mid >= l) ans = ask(rt << 1, l, r);
            if (r > mid) ans += ask(rt << 1 | 1, l, r);
            return ans;
        }
    } bit;
    
    bool cmp(const pair<PII, int>& a, const pair<PII, int>& b) {
        return a.fi.se < b.fi.se;
    }
    
    int main() {
        IOS; cin >> n; bit.build(1, 1, n);
        rep(i, 1, n) cin >> a[i];
        cin >> m;
        rep(i, 1, m) cin >> q[i].fi.fi >> q[i].fi.se, q[i].se = i;
        
        sort(q + 1, q + 1 + m, cmp);
        for (int i = 1, j = 1; i <= m; ++i) {
            for (; j <= q[i].fi.se; ++j) {
                if (ls[a[j]]) bit.change(1, ls[a[j]], -1);
                bit.change(1, j, 1); ls[a[j]] = j;
            }
            ans[q[i].se] = bit.ask(1, q[i].fi.fi, q[i].fi.se);
        }
        rep(i, 1, m) cout << ans[i] << '
    ';
        return 0;
    }
    

    莫队

    刚才是卡着 r 取修改区间, 离线

    那在线怎呢办呢?

    l~r 说白了就是 r - l + 1 - 重复数字的个数

    怎么区分 重复数字的问题,

    我们注意到 (a[i] == a[j], l < i < j < r) a[i]的下一个位置是 j, 而 j 的下一个位置 在 l~r内不存在, pre[i] 表示上一个

    知道怎么算了吧, 直接记 l~r a[i] 下个位置不在l~r区间内的数的个数即可, add 和 sub 的时候判断是否要+-贡献就行了

    const int N = 2e5 + 5, M = 1e6 + 5;
    
    int n, m, _, k;
    int h[M], pre[M], ne[M], ans[N];
    pair<PII, int> q[N];
    
    int add(int l, int r, int k) {
        if (k > r && pre[k] < l) return 1;
        if (k < l && ne[k] > r) return 1;
        return 0;
    }
    
    int sub(int l, int r, int k) {
        if (k == r && pre[k] < l) return 1;
        if (k == l && ne[k] > r) return 1;
        return 0;
    }
    
    int main() {
        IOS; cin >> n;
        rep(i, 1, 1e6) ne[i] = n + 1;
        rep(i, 1, n) cin >> m, pre[i] = h[m], ne[h[m]] = i, h[m] = i;
        cin >> m; int siz = sqrt(n);
        rep(i, 1, m) cin >> q[i].fi.fi >> q[i].fi.se, q[i].se = i;
        sort(q + 1, q + 1 + m, [&](pair<PII, int> a, pair<PII, int> b) {
            return a.fi.fi / siz == b.fi.fi / siz ? a.fi.se < b.fi.se : a.fi.fi / siz < b.fi.fi / siz;
        });
        int l = 1, r = 0, val = 0;
        rep(i, 1, m) {
            for (; q[i].fi.se > r; val += add(l, r, r + 1), ++r);
            for (; q[i].fi.se < r; val -= sub(l, r, r), --r);
            for (; q[i].fi.fi < l; val += add(l, r, l - 1), --l);
            for (; q[i].fi.fi > l; val -= sub(l, r, l), ++l);
            ans[q[i].se] = val;
        }
        rep(i, 1, m) cout << ans[i] << '
    ';
        return 0;
    }
    

    在线算法

    主席树

    跟莫队思路基本一样, 去掉了pre[N]数组, 并在线静在线回答

    const int N = 5e4 + 5, M = 1e6 + 5;
    
    struct CHT {
        struct node {
            int val, lson, rson;
            node (int Val = 0, int Ls = 0, int Rs = 0)
                : val(Val), lson(Ls), rson(Rs){}
        } tr[20 * N]; //log2(n)*n
    
        int root[N], tot;
    
        void update(int &x, int y, int l, int r, int k, int cnt) {
            tr[x = ++tot] = tr[y]; tr[x].val += cnt;
            if (l == r) return;
            int mid = l + r >> 1;
            if (k <= mid) update(tr[x].lson, tr[y].lson, l, mid, k, cnt);
            else update(tr[x].rson, tr[y].rson, mid + 1, r, k, cnt);
        }
    
        int ask(int x, int y, int l, int r, int k) {
            if (l >= k) return tr[x].val - tr[y].val;
            int mid = l + r >> 1, ans = 0;
            if (mid >= k) ans = ask(tr[x].lson, tr[y].lson, l, mid, k);
            return ans + ask(tr[x].rson, tr[y].rson, mid + 1, r, k);
        }
    } bit;
    
    int n, m, _, k;
    int h[M], ne[M];
    
    int main() {
        IOS; cin >> n;
        rep (i, 1, 1e6) ne[i] = n + 1;
        rep(i, 1, n) cin >> m, ne[h[m]] = i, h[m] = i;
        rep(i, 1, n) bit.update(bit.root[i], bit.root[i - 1], 1, n + 1, ne[i], 1);
    
        cin >> m;
        rep(i, 1, m) {
            int l, r; cin >> l >> r;
            cout << bit.ask(bit.root[r], bit.root[l - 1], 1, n + 1, r + 1) << '
    ';
        }
        return 0;
    }
    
  • 相关阅读:
    MongoDB 启动和关闭
    java protostuff 序列化反序列化工具
    第一天
    第六章
    第六章
    第六章
    第六章
    第五章---面向对象---1.软件开发/2.异常处理/3.try...except的详细用法
    第五章-面向对象-1.元类介绍/2.自定义元类控制类的行为/3.控制类的实例化行为/4.控制类的实例化行为的应用
    第五章---面向对象---1.绑定方法与非绑定方法介绍/2.绑定方法与非绑定方法应用/3.反射/4.内置方法
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/13884155.html
Copyright © 2011-2022 走看看