zoukankan      html  css  js  c++  java
  • kuangbin 莫队专题

    kuangbin 莫队专题

    这几天把框斌的莫队都刷完了, 还是有不少收获的, 之前不会证明莫对的时间复杂度, 现在也能口胡证明了。

    莫队和其它数据结构不一样,会了之后就很简单。只要是关于区间o(1)算出贡献都能用莫队解决。

    具体看下面的题目吧!

    Sona

    题解:求区间每个数出现的次数的立方和

    题解:刚学莫队的时候肯定知道求区间不同颜色的个数, 但是如何求不同颜色的个数的立方和了。

    这里有个和巧妙的做法就是, 用vis数组记录当前颜色出现的个数, 当出现颜色时, 先减去之前的个数

    立方和在加上现在颜色的立法和, 这样不断跟新最后一次一定是这个区间 这个颜色出现的总次数了。

    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    const int N = 1e5 + 7;
    struct query {
        int l, r, pos;
    }q[N];
    
    typedef long long ll;
    ll n, a[N], vis[N], res = 0, m, ans[N];
    int block[N];
    
    vector<ll> g;
    
    bool cmp(query x, query y) {
        if (block[x.l] == block[y.l]) {
            return x.r < y.r;
        }
        return block[x.l] < block[y.l];
    }
    
    int get_id(ll x) {
        return lower_bound(g.begin(), g.end(), x) - g.begin() + 1;
    }
    
    void add(int pos) {
        res -= vis[a[pos]] * vis[a[pos]] * vis[a[pos]];
        vis[a[pos]]++;
        res += vis[a[pos]] * vis[a[pos]] * vis[a[pos]];
    }
    
    void del(int pos) {
        res -= vis[a[pos]] * vis[a[pos]] * vis[a[pos]];
        vis[a[pos]]--;
        res += vis[a[pos]] * vis[a[pos]] * vis[a[pos]];
    }
    
    
    int main() {
    
        while(~scanf("%lld", &n)){
            g.clear();
            res = 0;
            for (int i = 0; i <= n + 10; i++) {
                vis[i] = 0;
            }
            int b = sqrt(n*1.0);
            for (int i = 1; i <= n; i++) {
                scanf("%lld", &a[i]);
                g.push_back(a[i]);
                block[i] = i / b;
            }
            sort(g.begin(), g.end());
            g.erase(unique(g.begin(), g.end()), g.end());
            for (int i = 1; i <= n; i++) {
                a[i] = get_id(a[i]);
            }
            scanf("%lld", &m);
            for (int i = 1; i <= m; i++) {
                scanf("%d %d", &q[i].l, &q[i].r);
                if (q[i].l > q[i].r) swap(q[i].l, q[i].r);
                q[i].pos = i;
            }
            sort(q + 1, q + m + 1, cmp);
            int l = 1, r = 0;
            for (int i = 1; i <= m; i++) {
                while (l < q[i].l) {
                    del(l++);
                }
                while (l > q[i].l) {
                    add(--l);
                }
                while (r < q[i].r) {
                    add(++r);
                }
                while (r > q[i].r) {
                    del(r--);
                }
                ans[q[i].pos] = res;
            }
            for (int i = 1; i <= m; i++) {
                printf("%lld
    ", ans[i]);
            }
        }
    
    
    }
    

    Little Elephant and Array

    题意:问区间有多少个数出现的次数是这个数本身

    题解:假设这个数字为x, 用vis数组记录每个数出现的个数, 如果刚好等于x那么答案加1如果刚好等于x + 1那么说明之前那个贡献就删掉 答案就减1.

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 7;
    struct query {
        int l, r, pos;
    }q[N];
    
    vector<ll> g;
    int n, m, block[N];
    ll a[N], ans[N], res = 0, vis[N];
    
    void add(int pos) {
        if (a[pos] > n) return;
        vis[a[pos]]++;
        if (vis[a[pos]] == a[pos]) {
            res++;
        } else if (vis[a[pos]] == a[pos] + 1) {
            res--;
        }
    }
    
    void del(int pos) {
        if (a[pos] > n) return; 
        vis[a[pos]]--;
        if (vis[a[pos]] == a[pos]) {
            res++;
        } else if (vis[a[pos]] == a[pos] - 1) {
            res--;
        }
    }
    
    
    bool cmp(query x, query y) {
        if (block[x.l] == block[y.l]) {
            return x.r < y.r;
        }
        return block[x.l] < block[y.l];
    }
    
    
    
    int main() {
        scanf("%d %d", &n, &m);
        int b = sqrt(n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            block[i] = i / b;
        }
    
        for (int i = 1; i <= m; i++) {
            scanf("%d %d", &q[i].l, &q[i].r);
            q[i].pos = i;
        }
    
        sort(q + 1, q + m + 1, cmp);
        int l = 1, r = 0;
        for (int i = 1; i <= m; i++) {
            while (l < q[i].l) {
                del(l++);
            }
            while (l > q[i].l) {
                add(--l);
            }
            while (r < q[i].r) {
                add(++r);
            }
            while (r > q[i].r) {
                del(r--);
            }
            ans[q[i].pos] = res;
        }
        for (int i = 1; i <= m; i++) {
            printf("%lld
    ", ans[i]);
        }
    }
    

    Machine Learning

    题意:给你n个数,让你求区间mex(0不算)和单点修改

    题解:带修莫队板子题吧, 带修莫队和普通莫队差不多多了一维时间, 每次暴力的时进行暴力修改, 然后要注意的是带修莫队分块是(n^{0.6666666})

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 1e6 + 7;
    
    struct query {
        int l, r, t, id;
    }q[N];
    
    struct option {
        int pos, v;
    }p[N], back[N];
    
    int block[N], ans[N], vis[N], maxn[N], n, m, a[N],A[N];
    
    bool cmp (query x, query y) {
        if (block[x.l] == block[y.l]) {
            if (block[x.r] == block[y.r]) {
                return x.t < y.t;
            }
            return x.r < y.r;
        }
        return x.l < y.l;
    }
    vector<int> g;
    
    int get_id(int x) {
        return lower_bound(g.begin(), g.end(), x) - g.begin() + 1;
    }
    
    void update(int pos, int value) {
        a[pos] = value;
    }
    
    void add(int pos) {
     
        maxn[vis[a[pos]]]--;
        vis[a[pos]]++;
        maxn[vis[a[pos]]]++;
    }
    
    void del(int pos) {
        maxn[vis[a[pos]]]--;
        vis[a[pos]]--;
        maxn[vis[a[pos]]]++;
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        int b = pow(n, 0.666666);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            A[i] = a[i];
            g.push_back(a[i]);
            block[i] = i / b;
        }
       
        int total = 0;
        int cnt = 1;
        for (int i = 1; i <= m; i++) {
            int op; scanf("%d", &op);
            if (op == 1) {
                scanf("%d %d", &q[cnt].l, &q[cnt].r);
                q[cnt].id = i;
                q[cnt].t = total;
                cnt++;
            } else {
                total++;
                scanf("%d %d", &p[total].pos, &p[total].v);
                back[total].v = A[p[total].pos];
                back[total].pos = p[total].pos;
                A[p[total].pos] = p[total].v;
                g.push_back(p[total].v);
            
            }
        }
        
        sort(g.begin(), g.end());
        g.erase(unique(g.begin(), g.end()), g.end());
        for (int i = 1; i <= n; i++) {
            a[i] = get_id(a[i]);
        }
        for (int i = 1; i <= total; i++) {
            p[i].v = get_id(p[i].v);
            back[i].v = get_id(back[i].v);
        }
        sort(q + 1, q + cnt, cmp);
        int l = 1, r = 0, now = 0;
        for (int i = 1; i < cnt; i++) {
            
            while (now < q[i].t) {
                now++;
                if (p[now].pos >= l && p[now].pos <= r) {
                    maxn[vis[a[p[now].pos]]]--;
                    vis[a[p[now].pos]]--;
                    maxn[vis[a[p[now].pos]]]++;
                    maxn[vis[p[now].v]]--;
                    vis[p[now].v]++;
                    maxn[vis[p[now].v]]++;
    
                }
                update(p[now].pos, p[now].v);
            }
            while (now > q[i].t) {
                if (l <= back[now].pos && r >= back[now].pos) {
                    maxn[vis[a[back[now].pos]]]--;
                    vis[a[back[now].pos]]--;
                    maxn[vis[a[back[now].pos]]]++;
                    maxn[vis[back[now].v]]--;
                    vis[back[now].v]++;
                    maxn[vis[back[now].v]]++;
                }
                update(back[now].pos, back[now].v);
                now--;
            }
            while (r < q[i].r) {
                add(++r);
            }
            while (l < q[i].l) {
                del(l++);
            }
            while (l > q[i].l) {
                add(--l);
            }
            while (r > q[i].r) {
                del(r--);
            }
    
            for (int j = 1; j <= 10000; j++) {
                if (maxn[j] == 0) {
                    ans[q[i].id] = j;
                    break;
                }
            }
        }
        for (int i = 1; i <= m; i++) {
            if (ans[i]) {
                printf("%d
    ", ans[i]);
            }
        }
    }
    
    
    
    

    Can you answer these queries II[https://vjudge.net/problem/SPOJ-GSS2]

    题意:求区间最大连续子串的和, 重复的数字只算一次。

    题解:这题不知道莫队咋求解, 网上貌似没用莫队的解法, 基本上都是用线段树解决这题的

    线段树操作这题第一步是离线, 将所有操作按照r进行从小到大排序。

    为啥要按照r从小到大排序?

    先想一下如何暴力求解?

    如果给你一个区间让你求最大子串和, 很多少人想的时(n ^ {2})暴力, 其实只需要 O(n)暴力就可以解决

    for (int i = l; i <= r; i++) {
    	sum += a[i];
    	ans = max(ans, sum);
    	sum = max(0, sum);
    }
    

    那能不能用线段树o(n) * log(n)维护所有区间答案呢?

    答案是可以的。

    假设线段现在插入的是第i个数那么

    第一个节点维护的信息是

    (a[1] + a[2] + a[3]+......+a[i])

    第二个节点维护的信息是

    (a[2] + a[3] + a[4] + ......+ a[i])

    第三个节点维护的信息是

    (a[3] + a[4] + ...... + a[i])

    第i个节点维护的信息是

    (a[i])

    那么插入到第i + 1个信息也是相当于区间1到i + 1加上a[i + 1]

    线段树每个节点还有在维护一个 从当前节点开始到i的所有子串的最大值

    如果线段树更新到第i个节点

    那么 询问操作中 r == i的答案一定在 l 到r中的最大值。

    因为线段树每个节点维护了从当前节点开始到i的所有子串的最大值。所以答案肯定在这个区间里面。

    这就是之前为啥询问要按照r从小到大排序。

    难点还是线段树的懒标记更新

    去重的话自己仔细想一下应该可以想出来。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 7;
    
    struct segment {
        ll sum, maxn;
    }tree[4 * N];
    
    #define m (l + r) / 2
    #define lson 2 * node
    #define rson 2 * node + 1
    
    ll a[N], ans[N];
    int flag[4 * N], fmaxn[4 * N];
    
    void push_down(int node) {
         if (fmaxn[node]) {
            tree[lson].maxn = max(tree[lson].maxn, fmaxn[node] + tree[lson].sum);
            tree[rson].maxn = max(tree[rson].maxn, tree[rson].sum + fmaxn[node]);
            fmaxn[lson] = max(fmaxn[lson], fmaxn[node] + flag[lson]);
            fmaxn[rson] = max(fmaxn[rson], fmaxn[node] + flag[rson]);
            fmaxn[node] = 0;
        }
        if (flag[node]) {
            tree[lson].sum += flag[node];
            tree[rson].sum += flag[node];
            flag[lson] += flag[node];
            flag[rson] += flag[node];
            flag[node] = 0;
        }
       
    }
    
    void update(ll v, int ql, int qr, int l, int r, int node) {
        if (ql <= l && qr >= r) {
    
            tree[node].sum += v;
            tree[node].maxn = max(tree[node].maxn, tree[node].sum);
            flag[node] += v;
            fmaxn[node] = max(fmaxn[node], flag[node]);
             
            return;
        }
        push_down(node);
        if (ql <= m) update(v, ql, qr, l, m, lson);
        if (qr > m) update(v, ql, qr, m + 1, r, rson);
        tree[node].sum = max(tree[lson].sum, tree[rson].sum);
        tree[node].maxn = max(tree[lson].maxn, tree[rson].maxn);
    }
    
    ll query(int ql, int qr, int l, int r, int node) {
        if (ql <= l && qr >= r) {
            return tree[node].maxn;
        }
        ll ans = INT_MIN;
        push_down(node);
        if (ql <= m) ans = max(ans, query(ql, qr, l, m, lson));
        if (qr > m) ans = max(ans, query(ql, qr, m + 1, r, rson));
        return ans;
    }
    
    map<int , int>vis;
    
    int pos[N];
    
    struct qu{
        int l, r, id;
    }p[N];
    
    int main() {
        int n, q;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            pos[i] = vis[a[i]];
            vis[a[i]] = i;
        }
        scanf("%d", &q);
        for (int i = 1; i <= q; i++) {
            int l, r; scanf("%d %d", &p[i].l, &p[i].r);
            p[i].id = i;
        }
    
        sort(p + 1, p + 1 + q, [](qu x, qu y) {
            return x.r < y.r;
        });
        int l = 1;
        for (int i = 1; i <= n; i++) {
            update(a[i], pos[i] + 1, i, 1, n, 1);
            while(p[l].r == i) {
                ans[p[l].id] = query(p[l].l, p[l].r, 1, n, 1);
                l++;
            }
        }
        for (int i = 1; i <= q; i++) {
            printf("%lld
    ", ans[i]);
        }
    
        
    
    
    
    }
    
  • 相关阅读:
    论JS函数传参时:值传递与引用传递的区别
    关于 rem
    致——自己
    用CSS3写的钟表
    HTML标签marquee实现滚动效果
    手机号截取
    CSS3绘制环形进度条
    限制内容长度(CSS,jQuery)
    移动web开发中遇到的一些问题收纳
    移动平台中 meta 标签的使用
  • 原文地址:https://www.cnblogs.com/BOZHAO/p/13856703.html
Copyright © 2011-2022 走看看