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\n", 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\n", 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\n", 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\n", ans[i]);
    }

    



}
posted @ 2020-10-22 10:28  ccsu_zhaobo  阅读(100)  评论(0编辑  收藏  举报