莫队

普通莫队

P2709 小B的询问 /【模板】莫队

题意:

小 B 有一个长为 \(n\) 的整数序列 \(a\),值域为 \([1,k]\)
他一共有 \(m\) 个询问,每个询问给定一个区间 \([l,r]\),求:

\(\sum\limits_{i=1}^k c_i^2\)

其中 \(c_i\) 表示数字 \(i\)\([l,r]\) 中的出现次数。

思路:

莫队板子

  • 将每个询问的 \(l\)\(r\) 存下来,方便离线处理

  • 将每个询问 \(l\) 所在的块编号作为第一关键字,将 \(r\) 作为第二关键字排序

为什么是按左端点所在块的编号? 因为这样可以保证 \(l\)\(r\) 指针没有较大的移动

  • 对于每个询问,我们移动 \(L\)\(R\) 指针,移动的过程中记录当前答案。当 \(L = l, R = r\) 时,记录答案,并处理下一个询问

    • \(l\) 指针向右移动时,删除最右端的数,减少答案

    • \(l\) 指针向左移动时,新加入数,增加答案

    • \(r\) 指针向右移动时,新加入数,增加答案

    • \(r\) 指针向左移动时,删除最左端的数,减少答案

  • 按输入顺序输出得到的结果

至于 \(c_i ^ 2\) 怎么处理,使用我们小学就学过的平方差公式,增加的时候增加 \(2 \times c_i + 1\),减少的时候减少 \(2 \times c_i - 1\) 即可。

由于每个块的大小不超过 \(O(\sqrt{n})\),所以总时间复杂度为 \(O(n\sqrt{n})\)

code:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int l, r, id;
} mo[50005];
int a[50005], b[50005], B, n, k, q, l = 1, r = 1, ll, rr;
long long c, ans[500005];
bool cmp(node a, node b)
{
    if ((a.l) / B != (b.l) / B)
        return a.l / B < b.l / B;
    return a.r < b.r;
}
void add(int x)
{
    c += 2 * b[x] + 1;
    b[x]++;
}
void del(int x)
{
    c -= 2 * b[x] - 1;
    b[x]--;
}

signed main()
{
    cin >> n >> q >> k;
    B = sqrt(n);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= q; i++)
    {
        cin >> mo[i].l >> mo[i].r;
        mo[i].id = i;
    }
    sort(mo + 1, mo + q + 1, cmp);
    for (int i = 1; i <= q; i++)
    {
        l = mo[i].l, r = mo[i].r;
        while (ll > l)
        {
            ll--;
            add(a[ll]);
        }
        while (rr < r)
        {
            rr++;
            add(a[rr]);
        }
        while (ll < l)
        {
            del(a[ll]);
            ll++;
        }
        while (rr > r)
        {
            del(a[rr]);
            rr--;
        }
        ans[mo[i].id] = c;
    }
    for (int i = 1; i <= q; i++)
    {
        cout << ans[i] - 1 << endl;
    }
}

P4462 [CQOI2018] 异或序列

题意:

已知一个长度为 \(n\) 的整数数列 \(a_1,a_2,\dots,a_n\),给定查询参数 \(l,r\),问在 \(a_l,a_{l+1},\dots,a_r\) 区间内,有多少子区间满足异或和等于 \(k\)。也就是说,对于所有的 \(x,y (l \leq x \leq y \leq r)\),能够满足 \(a_x \oplus a_{x+1} \oplus \dots \oplus a_y = k\)\(x,y\) 有多少组。

思路:

考虑维护一个前缀异或和 \(sum_i\),这样就可以在 \(O(1)\) 的时间复杂度下求出一段区间的异或和。

那么题目就转化为了区间有多少组数 \((l, r)\) 满足 \(sum_{l - 1} ⊕ sum_r = k\) 。稍微变一下就可以得到 \(sum_r ⊕ k = sum_{l - 1}\)

所以我们需要维护的东西就是 \(sum_{l - 1}\),由于这一道题值域不大,所以直接开个桶 \(cnt\) 存当前区间的前缀异或和,每次找 \(cnt_{sum_r} ⊕ k = sum_{l - 1}\) 的值就可以了。

这样可以做到单次移动端点 \(O(1)\),总时间复杂度 \(O(n\sqrt{n})\)

坑点:

  • long long

  • add中先更新ans,再更新桶,而del中先更新桶,再更新ans

  • 数组需要开到 2e5

  • \(l\) 初始化为 \(0\),因为查询区间是 \([0, n]\)

code:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int QWQ = 5e5 + 5;
int a[QWQ], pos[QWQ], L[QWQ], R[QWQ], n, m, k;
int block, num, cnt[QWQ], sum[QWQ], ans, q[QWQ];
struct node
{
    int l, r, id;
} w[QWQ];
bool cmp(node a, node b)
{
    if (pos[a.l] == pos[b.l])
    {
        return a.r < b.r;
    }
    return a.l < b.l;
}
void init()
{
    block = sqrt(n);
    num = n / block;
    for (int i = 1; i <= n; i++)
    {
        pos[i] = i / block + 1;
    }
    for (int i = 1; i <= num; i++)
    {
        L[i] = block * (i - 1) + 1;
        R[i] = block * i;
    }
    R[num] = n;
}
void add(int x)
{
    ans += cnt[a[x] ^ k];
    cnt[a[x]]++;
}
void del(int x)
{
    cnt[a[x]]--;
    ans -= cnt[a[x] ^ k];
}
signed main()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        a[i] ^= a[i - 1];
    }
    init();
    for (int i = 1; i <= m; i++)
    {
        cin >> w[i].l >> w[i].r;
        w[i].id = i;
    }
    sort(w + 1, w + m + 1, cmp);
    int l = 0, r = 0;
    cnt[0] = 1;
    for (int i = 1; i <= m; i++)
    {
        w[i].l = w[i].l - 1;
        while (l < w[i].l)
        {
            del(l ++);
        }
        while (l > w[i].l)
        {
            add(-- l);
        }
        while (r < w[i].r)
        {
            add(++r);
        }
        while (r > w[i].r)
        {
            del(r--);
        }
        q[w[i].id] = ans;
    }
    for (int i = 1; i <= m; i++)
    {
        cout << q[i] << endl;
    }
}
posted @ 2025-10-17 15:24  FurinaQWQ  阅读(6)  评论(0)    收藏  举报