莫队
莫队算法一般分为两类
- 莫队维护区间答案
- 维护区间内的数据结构
- 树上莫队,带修改莫队、二维莫队等等
普通莫队
- 将询问离线排序处理,使转移的次数尽量少
- 基于分块思想优化
- 若在其 l 在同块,那么将其 r 作为排序关键字
- 若 l 不在同块,就将 l 作为关键字排序
对于n与m同阶,一般可以设块长度为
复杂度
的移动在一个内,复杂度平摊为
的移动对于同一个块的最多移动 次,复杂度平摊为
排序方式
常用
将序列分成 个长度为 的块,若左端点在同一个块内,则按右端点排序
(以左端点所在块为第一关键字,右端点为第二关键字)
struct Node{
int left;
int right;
int id; //左右端点和id
friend bool operator <(const Node& a, const Node& b) {
return (a.left / block == b.left / block ? //若在同一个块,右端点从小到大
a.right < b.right : a.left < b.left); //左端点从小到大
}
}q[maxn];
奇偶性优化
指针移到右边后不用再跳回左边,而跳回左边后处理下一个块又要跳回右边
这样能减少一半操作,理论上能快一倍
struct Node{
int left;
int right;
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] ^ belong[b.left] ? belong[a.left] < belong[b.left] :
//若两者分块不同,从小到打排序
belong[a.left] & 1 ? a.right<b.right : a.right>b.right;
//按块的奇偶性,从左到有和从右到左
}
}q[maxn];
分块大小分析
分块时块的大小不是固定的,要根据题目具体分析(往往自以为被卡常,其实分块不对)
分析的过程以下方的过程为例
我们设块长度为 $block $
那么对于任意多个在同一块内的询问,挪动的距离就是
个块,移动的总次数就是
移动可能跨越块,所以还要加上一个 的复杂度,总复杂度为
我们要让这个值尽量小, 取 是最优的
复杂度为
转移
for (int i = 1; i <= m; i++) {
while (stdl > q[i].left) {
stdl--;
//operation 左端添加
}
while (stdr < q[i].right) {
stdr++;
//operation 右端添加
}
while (stdl < q[i].left) {
//operation 左端删除
stdl++;
}
while (stdr > q[i].right) {
//operation 右端删除
stdr--;
}
res[q[i].id] = ans;
}
模板题
P1494 小Z的袜子
HDU 6534 Chika and Friendly Pairs
题意
称
则称和为一对好数
给出数组,组询问
返回到区间内好数数量
思路
-
加入一个点,对答案产生的贡献为已知区间内好数的个数,可以树状数组求(下面讲)
可以离线--------------->莫队
-
树状数组 + 离散化能够维护区间的个数
- 每次加入一个节点,即为单点修改
- 对排序后,然后对每个二分,的区间,每次只要查询固定区间权值树状数组求和即可
如此实现的修改查询
-
莫队维护左右转移
同阶,分块取即可
随手再加个奇偶性优化
代码
树状数组
inline int lowbit(int x) {
return x & -x;
}
void modify(int x, int val) { //修改函数,1添加节点,-1删除节点
while (x <= n) {
tree[x] += val;
x += lowbit(x);
}
}
int query(int x) { //求和函数
int res = 0;
while (x) {
res += tree[x];
x -= lowbit(x);
}
return res;
}
离散化
for (int i = 1; i <= n; i++) {
a[i].data = io.read();
a[i].id = i;
p[i] = a[i]; //初始化权值
}
sort(p + 1, p + 1 + n); //排序,离散化
for (int i = 1; i <= n; i++)a[p[i].id].id = i;//返回每个数再权值数组中位置
二分预处理区间
void question(int id) {
int x = a[id].data;
//找到第一个大于等于x-k的数
int left = 1, right = n, mid; //初始化二分
int stdl, stdr, limit = max(0, x - k);
while (left <= right) {
mid = (left + right) >> 1;
if (p[mid].data >= limit)right = mid - 1, stdl = mid;
//若符合条件,使val尽量小
else left = mid + 1;
}
//找到最后一个小于x+k的数
left = 1, right = n, limit = x + k;
while (left <= right) {
mid = (left + right) >> 1;
if (p[mid].data <= limit)left = mid + 1, stdr = mid;
//若符合条件,使val尽量大
else right = mid - 1;
}
l[id] = stdl; r[id] = stdr;
}
莫队排序
struct Node{
int left;
int right;
int id;
friend bool operator <(const Node& a, const Node& b) {//奇偶性优化排序
return belong[a.left] ^ belong[b.left] ? belong[a.left] < belong[b.left] :
belong[a.left] & 1 ? a.right<b.right : a.right>b.right;
}
}q[maxn];
莫队
for (int i = 1; i <= m; i++) { //离线询问
q[i].left = io.read();
q[i].right = io.read();
q[i].id = i;
}
sort(q + 1, q + 1 + m); //分块排序
int stdl = 1, stdr = 0; LL ans = 0;
for (int i = 1; i <= m; i++) {
while (stdl > q[i].left) {
stdl--;
modify(a[stdl].id, 1);//添加左端点
ans = ans + query(r[stdl]) - query(l[stdl] - 1) - 1;//增加好数
}
while (stdr < q[i].right) {
stdr++;
modify(a[stdr].id, 1);//添加右端点
ans = ans + query(r[stdr]) - query(l[stdr] - 1) - 1;//增加好数
}
while (stdl < q[i].left) {
ans = ans - (query(r[stdl]) - query(l[stdl] - 1) - 1);//删除好数
modify(a[stdl].id, -1);//删除左端点
stdl++;
}
while (stdr > q[i].right) {
ans = ans - (query(r[stdr]) - query(l[stdr] - 1) - 1);//删除好数
modify(a[stdr].id, -1);//删除右端点
stdr--;
}
res[q[i].id] = ans;
}
AC
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int maxn = 50005;
class QIO {
public:
char buf[1 << 21], * p1 = buf, * p2 = buf;
int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
int read() {
int ret = 0, f = 0;
char ch = getc();
while (!isdigit(ch)) {
if (ch == '-')
f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = ret * 10 + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
} io;
int n, k, m;
struct Data{
int data;
int id;
friend bool operator <(const Data& a, const Data& b) {
return a.data < b.data;
}
}a[maxn], p[maxn];
int tree[maxn];
inline int lowbit(int x) {
return x & -x;
}
void modify(int x, int val) {
while (x <= n) {
tree[x] += val;
x += lowbit(x);
}
}
int query(int x) {
int res = 0;
while (x) {
res += tree[x];
x -= lowbit(x);
}
return res;
}
int l[maxn], r[maxn];
void question(int id) {
int x = a[id].data;
int left = 1, right = n, mid;
int stdl, stdr, limit = max(0, x - k);
while (left <= right) {
mid = (left + right) >> 1;
if (p[mid].data >= limit)right = mid - 1, stdl = mid;
else left = mid + 1;
}
left = 1, right = n, limit = x + k;
while (left <= right) {
mid = (left + right) >> 1;
if (p[mid].data <= limit)left = mid + 1, stdr = mid;
else right = mid - 1;
}
l[id] = stdl; r[id] = stdr;
}
int block, belong[maxn];
struct Node{
int left;
int right;
int id;
friend bool operator <(const Node& a, const Node& b) {
return belong[a.left] ^ belong[b.left] ? belong[a.left] < belong[b.left] :
belong[a.left] & 1 ? a.right<b.right : a.right>b.right;
}
}q[maxn];
LL res[maxn];
int main() {
n = io.read();
m = io.read();
k = io.read();
block = sqrt(n);
for (int i = 1; i <= n; i++) {
a[i].data = io.read();
a[i].id = i;
p[i] = a[i];
belong[i] = i / block;
}
sort(p + 1, p + 1 + n);
for (int i = 1; i <= n; i++)a[p[i].id].id = i;
for (int i = 1; i <= n; i++)question(i);
for (int i = 1; i <= m; i++) {
q[i].left = io.read();
q[i].right = io.read();
q[i].id = i;
}
sort(q + 1, q + 1 + m);
int stdl = 1, stdr = 0; LL ans = 0;
for (int i = 1; i <= m; i++) {
while (stdl > q[i].left) {
stdl--;
modify(a[stdl].id, 1);
ans = ans + query(r[stdl]) - query(l[stdl] - 1) - 1;
}
while (stdr < q[i].right) {
stdr++;
modify(a[stdr].id, 1);
ans = ans + query(r[stdr]) - query(l[stdr] - 1) - 1;
}
while (stdl < q[i].left) {
ans = ans - (query(r[stdl]) - query(l[stdl] - 1) - 1);
modify(a[stdl].id, -1);
stdl++;
}
while (stdr > q[i].right) {
ans = ans - (query(r[stdr]) - query(l[stdr] - 1) - 1);
modify(a[stdr].id, -1);
stdr--;
}
res[q[i].id] = ans;
}
for (int i = 1; i <= m; i++)
printf("%lld\n", res[i]);
}