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]);
        }
    
        
    
    
    
    }
    
  • 相关阅读:
    使用MobaXterm远程连接Ubuntu,启动Octave,界面不能正常显示
    ABP .Net Core 日志组件集成使用NLog
    ABP .Net Core Entity Framework迁移使用MySql数据库
    ABP前端使用阿里云angular2 UI框架NG-ZORRO分享
    阿里云 Angular 2 UI框架 NG-ZORRO介绍
    Visual Studio 2019 Window Form 本地打包发布猫腻
    VS Code + NWJS(Node-Webkit)0.14.7 + SQLite3 + Angular6 构建跨平台桌面应用
    ABP .Net Core 调用异步方法抛异常A second operation started on this context before a previous asynchronous operation completed
    ABP .Net Core To Json序列化配置
    .Net EF Core数据库使用SQL server 2008 R2分页报错How to avoid the “Incorrect syntax near 'OFFSET'. Invalid usage of the option NEXT in the FETCH statement.”
  • 原文地址:https://www.cnblogs.com/BOZHAO/p/13856703.html
Copyright © 2011-2022 走看看