莫队_Part four
回滚莫队
前置知识:莫队。
与树上和带修没什么关系qwq
回滚莫队分为两种:只删不增莫队和只增不删莫队。
突然变的没有那么高大上了🥀
其实确实如此。
回滚莫队只是在普通莫队的基础上增加了撤销操作。它会让程序不出现删除或者插入操作。
在平时的莫队中,插入和删除的复杂度理论上是一样的,但是有些时候插入代码复杂程度要远远高于删除或者删除高于插入,这时候最好的方法就是去掉一种操作,所以这时候回滚莫队闪亮登场qnq
用几个简单的例子理解一下回滚莫队的奇妙之处/fad
区间众数
给定一个序列和若干次询问,每次询问给定左右端点,打印众数,可离线。
这是一个经典的根号问题qwq,可以用分块之类的东西搞,但是分块太神了把持不住,所以我们使用回滚莫队~o( =∩ω∩= )m
首先考虑莫队,用cnt数组表示一个数的出现次数,当插入一个数时, 显然只需要与最大值比较一下更新即可,但是当我们要删除的时候,让cnt--?与次大值比较?……太麻烦了,所以我们考虑如何避免删除元素。
首先,对于左右端点位于同一个块中的,直接暴力,复杂度 \(O( \sqrt n )\),显然正确
接下来对于不满足以上条件的询问,按左端点排序,左端点在一个块中的按右端点排序,左端点在同一块的一起处理。
对每个块中的询问分别处理,因为右端点单调递增,所以可以只包含增加操作。
这样从左端点所在块的右端点到询问的右端点之间的答案就计算出来了。
对于左端点到左端点所在块的右端点这个区间,我们考虑这样一种做法,将左指针移动到左端点所在块的右端点 + 1处,向左插入,将块内的贡献加入。
每个询问处理完后,将答案变为处理左端点到左端点所在块的右端点之前的答案,改操作不是删除操作,我们称之为撤销qwq
右端点复杂度 \(O(n\sqrt n)\),左端点复杂度 \(O(m\sqrt n)\),复杂度正确,然后这个题我们就切了,蜡笔题qnq
下一道
题目就是说,给你个序列,然后让你求每个元素出现次数×元素值的最大值。
区间众数模板qwq
/**
* author: zcxxxxx
* creater: 2022.8.7
**/
#include <bits/stdc++.h>
#define il inline
#define reg register
#define ll long long
#define int long long
#define pii pair<int, int>
#define eps 1e-8
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
namespace math {
ll Gcd(ll num1, ll num2) {return !num2 ? num1 : Gcd(num2, num1 % num2);}
ll tx, ty, tz; void Exgcd(ll num1, ll num2) {if(num2 == 0) {tx = 1, ty = 0; return;}
Exgcd(num2, num1 % num2), tz = tx, tx = ty, ty = tz - num1 / num2 * ty;}
ll Gmul(ll x, ll y) {ll ans = 0; while(y != 0) {if(y & 1) ans = (ans + x) % mod; x = (x + x) % mod, y >>= 1;} return ans;}
ll Gpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = Gmul(ans, base); base = Gmul(base, base);pow >>= 1;} return ans;}
ll Qpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = (ans * base) % mod; base = (base * base) % mod;pow >>= 1;} return ans;}
bool cmpstb(ll a, ll b) {return a < b;}
}
namespace rdin {
inline int READ() {register int x = 0, t = 1; register char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar(); if(ch == '-') {t = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}return x * t;}
template <typename T>
void read(T& arg) {arg = READ();}
template <typename T, typename...Types>
void read(T& a, Types&...as) {a = READ(); read(as...);}
}
using namespace std;
using namespace rdin;
//using namespace math;
const int A = 1e2 + 7;
const int B = 1e3 + 7;
const int C = 1e5 + 7;
const int D = 3e5 + 7;
const int E = 1e6 + 7;
const int F = 1e7 + 7;
#define FOR(x,y,z) for(register int x=y;x<=z;++x)
#define FOD(x,y,z) for(register int x=y;x>=z;--x)
/*----------------------------------------*/
int n, m, block, mx, len, del, lst;
bool flag;
int bl[D], a[D], c[D], lsh[D], cnt[D], L[C], R[C], ans[D];
struct query {
int l, r, id;
}q[D];
bool cmp (query a, query b) {
if (bl[a.l] == bl[b.l]) return a.r < b.r;
return a.l < b.l;
}
int js (int l, int r) {
mx = 0;
for (int i = l; i <= r; ++ i) {
++ cnt[c[i]];
mx = max (a[i] * cnt[c[i]], mx);
}
for (int i = l; i <= r; ++ i)
cnt[c[i]] --;
return mx;
}
signed main () {
read (n, m);
block = sqrt (n);
int lxl = 1;
L[1] = 1;
while (L[lxl] + block < n) {
R[lxl] = L[lxl] + block - 1;
++ lxl;
L[lxl] = R[lxl - 1] + 1;
}
R[lxl] = n;
for (int i = 1; i <= lxl; ++ i)
for (int j = L[i]; j <= R[i]; ++ j)
bl[j] = i;
for (int i = 1; i <= n; ++ i)
read (a[i]), lsh[i] = c[i] = a[i];
sort (lsh + 1, lsh + 1 + n);
len = unique (lsh + 1, lsh + 1 + n) - lsh - 1;
for (int i = 1; i <= n; ++ i)
c[i] = lower_bound (lsh + 1, lsh + 1 + len, c[i]) - lsh;
for (int i = 1; i <= m; ++ i) {
read (q[i].l, q[i].r); q[i].id = i + del;
if (bl[q[i].l] == bl[q[i].r]) {
ans[i + del] = js (q[i].l, q[i].r);
i --, m --, del ++;
}
}
sort (q + 1, q + 1 + m, cmp);
int l, r;
for (int i = 1; i <= m; ++ i) {
if (bl[q[i].l] != bl[q[i - 1].l] || i == 1) flag = 1;
if (flag) {
memset (cnt, 0, sizeof cnt);
r = R[bl[q[i].l]];
mx = lst = 0;
flag = 0;
}
while (r < q[i].r) {
r ++;
cnt[c[r]] ++;
mx = lst = max (lst, cnt[c[r]] * a[r]);
}
l = R[bl[q[i].l]] + 1;
while (l > q[i].l) {
l --;
cnt[c[l]] ++;
mx = max (mx, cnt[c[l]] * a[l]);
}
ans[q[i].id] = mx;
mx = lst;
l = R[bl[q[i].l]] + 1;
while (l > q[i].l) {
l --;
cnt[c[l]] --;
}
}
for (int i = 1; i <= m + del; ++ i)
cout << ans[i] << "\n";
return 0;
}
接着讲模板题
简述一下就是,让你求区间相同元素的最远间隔距离。
第一眼看到这个题,就想到维护fir[x]和las[x],分别表示到当前这个状态(指某个区间[l, r])x元素第一次出现的位置和最后一次出现的位置。
然后每次到一个位置,该位置元素为x,更新fir[x]并与las[x]作差更新答案,或者更新las[x]并与fir[x]作差更新答案。
然后做完了
/**
* author: zcxxxxx
* creater: 2022.8.6
**/
#include <bits/stdc++.h>
#define il inline
#define reg register
#define ll long long
#define pii pair<int, int>
#define eps 1e-8
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
namespace math {
ll Gcd(ll num1, ll num2) {return !num2 ? num1 : Gcd(num2, num1 % num2);}
ll tx, ty, tz; void Exgcd(ll num1, ll num2) {if(num2 == 0) {tx = 1, ty = 0; return;}
Exgcd(num2, num1 % num2), tz = tx, tx = ty, ty = tz - num1 / num2 * ty;}
ll Gmul(ll x, ll y) {ll ans = 0; while(y != 0) {if(y & 1) ans = (ans + x) % mod; x = (x + x) % mod, y >>= 1;} return ans;}
ll Gpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = Gmul(ans, base); base = Gmul(base, base);pow >>= 1;} return ans;}
ll Qpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = (ans * base) % mod; base = (base * base) % mod;pow >>= 1;} return ans;}
bool cmpstb(ll a, ll b) {return a < b;}
}
namespace rdin {
inline int READ() {register int x = 0, t = 1; register char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar(); if(ch == '-') {t = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}return x * t;}
template <typename T>
void read(T& arg) {arg = READ();}
template <typename T, typename...Types>
void read(T& a, Types&...as) {a = READ(); read(as...);}
}
using namespace std;
using namespace rdin;
//using namespace math;
const int A = 1e2 + 7;
const int B = 1e3 + 7;
const int C = 1e5 + 7;
const int D = 3e5 + 7;
const int E = 1e6 + 7;
const int F = 1e7 + 7;
#define FOR(x,y,z) for(register int x=y;x<=z;++x)
#define FOD(x,y,z) for(register int x=y;x>=z;--x)
/*----------------------------------------*/
int n, m, len, block, mx, lst, del;
int a[D], lsh[D], c[D], id[D], ans[D], L[D], R[D], fir[D], las[D], tmpfir[D], tmplas[D];
bool flag;
struct node {
int l, r, id;
}q[D];
bool cmp (node a, node b) {
if (id[a.l] == id[b.l]) return id[a.r] < id[b.r];
return id[a.l] < id[b.l];
}
void js (int l, int r) {
mx = 0;
for (int i = l; i <= r; ++ i) {
if (!fir[c[i]])
fir[c[i]] = i;
else {
mx = max (mx, abs (i - fir[c[i]]));
}
}
for (int i = l; i <= r; ++ i)
fir[c[i]] = 0;
}
int main () {
read (n);
block = sqrt (n);
for (int i = 1; i <= n; ++ i)
read (a[i]), lsh[i] = c[i] = a[i];
sort (lsh + 1, lsh + 1 + n);
len = unique (lsh + 1, lsh + 1 + n) - lsh - 1;
for (int i = 1; i <= n; ++ i)
c[i] = lower_bound (lsh + 1, lsh + 1 + len, c[i]) - lsh;
int lxl = 1;
L[1] = 1;
while (L[lxl] + block < n) {
R[lxl] = L[lxl] + block - 1;
lxl ++;
L[lxl] = R[lxl - 1] + 1;
}
R[lxl] = n;
for (int i = 1; i <= lxl; ++ i)
for (int j = L[i]; j <= R[i]; ++ j)
id[j] = i;
read (m);
for (int i = 1; i <= m; ++ i) {
read (q[i].l, q[i].r); q[i].id = i + del;
if (id[q[i].l] == id[q[i].r]) {
mx = 0;
js (q[i].l, q[i].r);
ans[i + del] = mx;
i --, m --, del ++;
}
}
sort (q + 1, q + 1 + m, cmp);
int l, r;
for (int i = 1; i <= m; ++ i) {
if (id[q[i].l] != id[q[i - 1].l] || i == 1) flag = 1;
if (flag) {
memset(fir, 0, sizeof fir);
memset(las, 0, sizeof las);
memset(tmpfir, 0, sizeof tmpfir);
memset(tmplas, 0, sizeof tmplas);
r = R[id[q[i].l]];
mx = lst = 0;
flag = 0;
}
while (r < q[i].r) {
r ++;
if (!fir[c[r]])
fir[c[r]] = tmpfir[c[r]] = r;
else
mx = lst = max (lst, abs(r - fir[c[r]]));
if (!las[c[r]])
las[c[r]] = tmplas[c[r]] = r;
else mx = lst = max (lst, abs(r - las[c[r]]));
mx = lst = max (lst, abs(fir[c[r]] - las[c[r]]));
}
l = R[id[q[i].l]] + 1;
while (l > q[i].l) {
l --;
if (!tmpfir[c[l]])
tmpfir[c[l]] = l;
else
mx = max (mx, abs(l - tmpfir[c[l]]));
if (!tmplas[c[l]])
tmplas[c[l]] = l;
else
mx = max (mx, abs(l - tmplas[c[l]]));
mx = max (mx, abs (tmpfir[c[l]] - tmplas[c[l]]));
}
ans[q[i].id] = mx;
mx = lst;
l = R[id[q[i].l]] + 1;
while (l > q[i].l) {
l --;
tmpfir[c[l]] = fir[c[l]];
tmplas[c[l]] = las[c[l]];
}
}
for (int i = 1; i <= m + del; ++ i)
cout << ans[i] << "\n";
return 0;
}