莫隊
莫隊算法一般分爲兩類
- 莫隊維護區間答案
- 維護區間內的數據結構
- 樹上莫隊,帶修改莫隊、二維莫隊等等
普通莫隊
- 將詢問離線排序處理,使轉移的次數儘量少
- 基於分塊思想優化
- 若在其 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]);
}