莫队算法
普通莫队
对于询问奇偶分块
bool cmp(nd x,nd y)
{
int lb = x.x / bl,rb = y.x / bl;
if (lb ^ rb) return x.x < y.x; // l,r不同块以l排序
return lb & 1 ? (x.y > y.y) : (x.y < y.y); //同则看l在什么块内
}
bool cmp(nd x,nd y){return (x.l / bl) ^ (y.l / bl) ? (x.l < y.l) : (((x.l / bl) & 1) ? x.r < y.r : x.r > y.r);}//这是另一打法
其中\(bl\)为块长,一般为\(\frac{n}{\sqrt{\frac{2}{3}m}}\)
接着即可开双指针乱移即可。
【GDKOI2014】小纪的作业题
这是一道比较模板的莫队,开个桶计次数,一个一个数加入或删除即可
$\text{Code}$
#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 1e5 + 5,P = 1e9 + 7;
int n,m,L,R,bl,d[N];
LL ans,v[N],a[N],mi[N],as[N];
struct nd{int x,y,id;}b[N];
bool cmp(nd x,nd y)
{
int lb = x.x / bl,rb = y.x / bl;
if (lb ^ rb) return x.x < y.x;
return lb & 1 ? (x.y > y.y) : (x.y < y.y);
}
void Mod(LL &x,LL y){x = (x + y >= P ? x + y - P : x + y);}
LL fpow(int x,LL y)
{
LL res = 1;
for (; x; x >>= 1,y = y * y % P)
if (x & 1) res = res * y % P;
return res;
}
void Insert(int x)
{
LL g1 = v[a[x]],g2 = v[a[x]] * a[x] % P;
if (!d[a[x]]) g1 = 0;
Mod(ans,P - g1),Mod(ans,g2),d[a[x]]++,v[a[x]] = v[a[x]] * a[x] % P;
}
void Delete(int x)
{
LL g1 = v[a[x]] * mi[a[x]] % P,g2 = v[a[x]];
if (d[a[x]] <= 1) g1 = 0;
Mod(ans,g1),Mod(ans,P - g2),d[a[x]]--,v[a[x]] = v[a[x]] * mi[a[x]] % P;
}
LL work(int l,int r)
{
while (R < r) R++,Insert(R);
while (L > l) L--,Insert(L);
while (R > r) Delete(R),R--;
while (L < l) Delete(L),L++;
return ans;
}
int main()
{
scanf("%d%d",&n,&m),bl = n / (sqrt(2 * m / 3));
for (int i = 1; i <= n; i++) scanf("%d",&a[i]),mi[a[i]] = fpow(P - 2,a[i]),v[a[i]] = 1;
for (int i = 1; i <= m; i++) scanf("%d%d",&b[i].x,&b[i].y),b[i].id = i;
sort(b + 1,b + 1 + m,cmp); L = 1,R = 0;
for (int i = 1; i <= m; i++) as[b[i].id] = work(b[i].x,b[i].y);
for (int i = 1; i <= m; i++) printf("%lld\n",as[i]);
}
树上莫队
就是在树上跑莫队,对于序列很好去跑莫队,那树呢?
没错,就是将树变成序列,与\(LCA\)有关的问题常常以树的欧拉序来跑莫队。
带修莫队
对于修改,我们可以将我们的指针加一维\(time\),在移动时有一些小细节需注意。
这是我们的块长\(block = n^{\frac{2}{3}}\),总时间为\(O(n^{\frac{5}{3}})\)。
排序以三个关键字即可。
P1903 [国家集训队] 数颜色 / 维护队列
这题是模板题,注意下\(time\)指针移动时的细节即可。
$\text{Code}$
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5e4 + 5;
int a[N],c[N],d[(int)1e6 + 5],res,n,m,cnt,bl,ans[N],g[N]; char s[10];
struct nd{int l,r,t,id;}b[N];
void Add(int x,int v)
{
if (!d[x] && v == 1) res++;
if (d[x] == 1 && v == -1) res--;
d[x] += v;
}
void change(int T,int l,int r)
{
if (!g[T]) return;
if (l <= g[T] && g[T] <= r) Add(a[g[T]],-1),Add(c[T],1);
swap(a[g[T]],c[T]);
}
bool cmp(nd x,nd y)
{
if ((x.l / bl) ^ (y.l / bl)) return x.l < y.l;
if ((x.r / bl) ^ (y.r / bl)) return x.r < y.r;
return x.t < y.t;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++) scanf("%d",&a[i]);
for (int i = 1,T = 1,q,p; i <= m; i++)
{
scanf("%s%d%d",s,&q,&p);
if (s[0] == 'R') T++,g[T] = q,c[T] = p;
else cnt++,b[cnt] = nd{q,p,T,cnt};
}
bl = pow(n,2.0 / 3.0),sort(b + 1,b + 1 + cnt,cmp);
for (int i = 1,L = 1,R = 0,T = 0; i <= cnt; i++)
{
while (R < b[i].r) Add(a[++R],1);
while (L > b[i].l) Add(a[--L],1);
while (R > b[i].r) Add(a[R--],-1);
while (L < b[i].l) Add(a[L++],-1);
while (T < b[i].t) T++,change(T,L,R);
while (T > b[i].t) change(T,L,R),T--;
ans[b[i].id] = res;
}
for (int i = 1; i <= cnt; i++) printf("%d\n",ans[i]);
}
回滚莫队
在进行莫队时,我们可能会发现在加数时时间复杂度是\(O(1)\)的,但在删数时复杂度是很高的,或不可做的,那么我们就可以考虑只加不删,来看一道例题理解。
「JOISC 2014 Day1」历史研究
这题中删数是复杂的,那么我们可以考虑将原序列进行分块,对询问按以左端点所属块编号升序为第一关键字,右端点升序为第二关键字的方式排序,设块长为\(B\)。
对于在同一块的\(l,r\)可以暴力做,时间为\(O(B)\)
对于同一块的\(l\)一起做,初始将指针\(L,R\)移到块尾,因为这时\(r\)是递增的,指针\(R\)可以一直向右移加数。而\(L\)指针向左移后,要再移回原来的位置,因为\(L\)最多移动\(B\)次,所以时间也为\(O(B)\)。
在换块是要将\(L,R\)内的数清空,时间为\(O(n)\),但只会换\(O(\frac{n}{B})\)次。
总时间复杂度为\(O(nB + \frac{n^2}{B})\),当\(B= \sqrt{n}\)时最优为\(O(n\sqrt{n})\)。
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#define IN inline
#define LL long long
using namespace std;
const int N = 1e5 + 5;
int n, m, B, bl[N], ed[N], a[N], X[N], g[N], d[N]; LL ans[N];
IN int read() {
int t = 0,res = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) t |= (ch == '-');
for (; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ 48);
return t ? -res : res;
}
struct qy{int l, r, id;}b[N];
bool cmp(qy x, qy y){return bl[x.l] ^ bl[y.l] ? x.l < y.l : x.r < y.r;}
int main() {
n = read(), m = read(), B = sqrt(n);
for (int i = 1; i <= n; i++) a[i] = read(), X[i] = a[i];
sort(X + 1, X + 1 + n); int len = unique(X + 1, X + 1 + n) - X - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(X + 1, X + 1 + len, a[i]) - X;
for (int i = 1; i <= m; i++) b[i] = qy{read(), read(), i};
for (int i = 1; i <= n; i++) bl[i] = i / B;
ed[0] = B - 1, ed[n / B] = n;
for (int i = 1; i < n / B; i++) ed[i] = ed[i - 1] + B;
sort(b + 1, b + 1 + m, cmp);
int l = 1, r = 0, pre = -1; LL res = 0;
for (int i = 1; i <= m; i++) {
if (bl[b[i].l] == bl[b[i].r]) {
for (int j = b[i].l; j <= b[i].r; j++) g[a[j]]++;
for (int j = b[i].l; j <= b[i].r; j++)
ans[b[i].id] = max(ans[b[i].id], (LL)g[a[j]] * X[a[j]]), g[a[j]] = 0;
continue;
}
if (pre != bl[b[i].l]) {
for (int j = l; j <= r; j++) d[a[j]]--;
res = 0, pre = bl[b[i].l], r = ed[pre], l = r + 1;
}
while (r < b[i].r) r++, d[a[r]]++, res = max(res, (LL)d[a[r]] * X[a[r]]);
ans[b[i].id] = res; int pl = l;
while (pl > b[i].l)
pl--, d[a[pl]]++, ans[b[i].id] = max(ans[b[i].id], (LL)d[a[pl]] * X[a[pl]]);
while (pl < l) d[a[pl]]--, pl++;
}
for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
}

浙公网安备 33010602011771号