[BZOJ3781][P2709]小B的询问[莫队]

Posted ycrpro

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3781][P2709]小B的询问[莫队]相关的知识,希望对你有一定的参考价值。

入门题

对于一个区间的询问,如果在已知\\([l,r]\\)的答案时可以用O(1)的时间求出左右端点\\(±1\\)的答案,就可以使用莫队来优化。

设已知区间为\\([l_1,r_1]\\),所求区间为\\([l_2,r_2]\\)
可知求得\\([l_2,r_2]\\)的成本是\\(|l_1-l_2| + |r_1-r_2|\\)如果把这两个区间看成点,这个成本就是两点的曼哈顿距离
对于多个询问,求出曼哈顿距离最小生成树就可以以最小成本获得答案,使用一种奇怪的方式 ——sort来获得最优的转移方式 我并不知道原理

对于整个区间进行分块,每个询问按照块编号为第一关键字,右端点为第二关键字进行排序,然后依次处理每个询问即可。看起来还是很暴力

卡常技巧我仍然不知道为什么:块大小\\(\\frac{n}{\\sqrt{m*\\frac{2}{3}}}\\)\\(\\sqrt n\\)更快,sort时对于同一块里的询问按照奇偶排序更快
效果图
技术分享图片

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005; typedef long long LL;
struct Data{
  int l, r, id, pos;
  inline bool operator < (const Data & rhs) const {
    return pos ^ rhs.pos ? pos < rhs.pos : pos&1?r<rhs.r:r>rhs.r;
    // if (pos == rhs.pos) return r < rhs.r;
    // return pos < rhs.pos;
  }
}d[MAXN];

int b[MAXN], a[MAXN], n, m, k, cnt[MAXN];LL anss[MAXN];

int main(void) {
  scanf("%d%d%d", &n, &m, &k);
  int block = n/sqrt(m*2/3);
  for(int i = 1; i <= n; ++i) scanf("%d", a+i);
  for(int i = 1; i <= m; ++i) {
    scanf("%d%d", &d[i].l, &d[i].r); d[i].id = i;
    d[i].pos = (d[i].l-1)/block + 1;
  }
  sort(d+1, d+m+1);
  int l = 1, r = 0; LL ans = 0;
  for(int i = 1; i <= m; ++i) {
    while (l > d[i].l) l--, cnt[a[l]]++, ans += 2*cnt[a[l]] - 1;
    while (r < d[i].r) r++, cnt[a[r]]++, ans += 2*cnt[a[r]] - 1;
    while (l < d[i].l) cnt[a[l]]--, ans -= 2*cnt[a[l]] + 1, l++;
    while (r > d[i].r) cnt[a[r]]--, ans -= 2*cnt[a[r]] + 1, r--;
    anss[d[i].id] = ans;
  }
  for(int i = 1; i <= m; ++i) printf("%lld\\n", anss[i]);
  return 0;
}

以上是关于[BZOJ3781][P2709]小B的询问[莫队]的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj 3781: 小B的询问 莫队,分块,暴力

bzoj 3781 小B的询问 —— 莫队

bzoj3781小B的询问*

bzoj 3781 小B的询问——分块

bzoj3781 小B的询问

P2709 小B的询问 莫队