算法学习笔记(3):莫队算法
1.算法学习笔记(1):CDQ分治2.算法学习笔记(2):分块
3.算法学习笔记(3):莫队算法
4.算法学习笔记(4):FHQ平衡树(无旋平衡树)5.算法学习笔记(5):AC自动机6.算法学习笔记(6):优秀trick和性质合集7.算法学习笔记(7):数论8.算法学习笔记(9):第k大问题合集9.算法学习笔记(10):各种序的美好性质10.算法学习笔记(11):历史版本和线段树11.算法学习笔记(12):左偏树12.算法学习笔记(13):同余最短路13.算法学习笔记(14):区间最值操作和历史最值问题14.算法学习笔记(15): Splay树15.算法学习笔记(16):Link Cut Tree16.算法学习笔记(17):Slope trick17.算法学习笔记(18):珂朵莉树18.算法学习笔记(20):网络流19.算法学习笔记(21):数论分块20.算法学习笔记(22):莫比乌斯反演21.算法学习笔记(23):杜教筛22.算法学习笔记(24):筛法23.暑假集训学习笔记(1):lxl DS Day 124.暑假集训学习笔记(2):lxl DS Day 225.暑假集训学习笔记(3):lxl DS Day 326.多项式笔记27.生成函数笔记28.插头DP29.DP选讲做题记录 by 付乙淼30.拓展摩尔投票31.图论知识总结莫队算法
莫队算法是由莫涛发明的离线算法, 有很多应用 , 实现起来也较为简单, 可以称作优雅的暴力, 大部分情况是将所有询问离线下来,通过合理的排序, 然后进行
普通莫队
直接上题吧
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 5e4 + 10;
int n, m, raw[N], block, cnt[N];
struct opt{
int l, r, id;
bool operator < (const opt &x) const {
if (l / block != x.l / block) return l < x.l;
return ((l / block) & 1) ? r < x.r : r > x.r;
}
}a[N];
#define lint long long
lint sum;
void add(int k) {
sum += cnt[k];
cnt[k]++;
}
void del(int k) {
cnt[k]--;
sum -= cnt[k];
}
lint gcd(lint a, lint b) {
return b ? gcd(b, a % b) : a;
}
lint ans1[N], ans2[N];
int main() {
scanf("%d%d", &n, &m);
block = sqrt(n);
for (int i = 1; i <= n; i++) scanf("%d", &raw[i]);
for (int i = 1; i <= m; i++) { scanf("%d%d", &a[i].l, &a[i].r); a[i].id = i; }
sort(a + 1, a + 1 + m);
// for (int i = 1; i <= m; i++) {
// printf("%d %d %d\n", a[i].l, a[i].r, a[i].id);
//
// }
// puts("fuck");
for (int i = 1, l = 1, r = 0; i <= m; i++) {
// printf("%d\n", i);
// printf("%d %d\n", l, r);
int pos = a[i].id;
if (a[i].l == a[i].r) {
ans1[pos] = 0, ans2[pos] = 1;
continue;
}
while (l > a[i].l) add(raw[--l]);
while (r < a[i].r) add(raw[++r]);
while (l < a[i].l) del(raw[l++]);
while (r > a[i].r) del(raw[r--]);
ans1[pos] = sum;
ans2[pos] = 1ll * (r - l + 1) * (r - l) / 2;
}
for (int i = 1; i <= m; i++) {
if (ans1[i] == 0 && ans2[i] == 1) {
printf("%lld/%lld\n", ans1[i], ans2[i]);
continue;
}
else {
lint _gcd = gcd(ans1[i], ans2[i]);
printf("%lld/%lld\n", ans1[i] / _gcd, ans2[i] / _gcd);
}
}
return 0;
}
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 2e5 + 10;
int n, m, raw[N], val[N], block;
struct opt{
int l, r, id;
bool operator < (const opt &x) const{
if (l / block != x.l / block) return l < x.l;
return ((l / block) & 1) ? r < x.r : r > x.r;
}
}a[N];
void disc() {
for (int i = 1; i <= n; i++) val[i] = raw[i];
sort(raw + 1, raw + 1 + n);
int cnt = unique(raw + 1, raw + 1 + n) - raw - 1;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(raw + 1, raw + 1 + cnt, val[i]) - raw;
val[i] = pos;
}
}
int maxn = 0, num[N], cnt[N], ans[N];
inline void add(int x) {
if (cnt[val[x]] + 1 > maxn) maxn++;
num[cnt[val[x]]]--, cnt[val[x]]++, num[cnt[val[x]]]++;
}
inline void del(int x) {
if (num[maxn] == 1 && cnt[val[x]] == maxn) maxn--;
num[cnt[val[x]]]--, cnt[val[x]]--, num[cnt[val[x]]]++;
}
int main() {
scanf("%d%d", &n, &m);
block = sqrt(n);
for (int i = 1; i <= n; i++) scanf("%d", &raw[i]);
disc();
for (int i = 1; i <= m; i++) {
scanf("%d%d", &a[i].l, &a[i].r);
a[i].id = i;
}
sort(a + 1, a + 1 + m);
for (int i = 1, l = 1, r = 0; i <= m; i++) {
while (l < a[i].l) del(l++);
while (l > a[i].l) add(--l);
while (r > a[i].r) del(r--);
while (r < a[i].r) add(++r);
ans[a[i].id] = maxn;
}
for (int i = 1; i <= m; i++) printf("%d\n", -ans[i]);
return 0;
}
带修莫队
简而言之, 是加上时间的一维, 并且把
上题吧
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
namespace Input {
int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void write(int x) {
if (x < 10) {
putchar(x + 48);
return;
}
write(x / 10);
putchar(x % 10 + 48);
}
}using namespace Input;
const int N = 133333 + 10;
//const int NN = 150;
int n, m, col[N], block;
struct Query{
int x, y, tim, id;
bool operator < (const Query &a) const{
if (x / block == a.x / block) {
if (y / block == a.y / block) return (y / block) & 1 ? tim < a.tim : tim > a.tim;
return y / block < a.y / block;
}
return x / block < a.x / block;
}
// Query(int _x, int _y, int _tim, int _id) { x = _x, y = _y, tim = _tim, id = _id; }
}Q[N];
struct Change{
int x, y;
}R[N];
int qcnt, rcnt, cnt[1000010], sum, ans[N];
// op 1 = Q 0 = R;
void add(int x) {
if (!cnt[x]) sum++;
cnt[x]++;
}
void del(int x) {
cnt[x]--;
if (!cnt[x]) sum--;
}
int main() {
// write(18530853);
// scanf("%d%d", &n, &m);
n = read(), m = read();
block = pow(n, 2.0 / 3.0);
for (int i = 1; i <= n; i++) col[i] = read();
// scanf("%d", col[i]);
char st[5];
for (int i = 1; i <= m; i++) {
int x, y; scanf("%s", st);
x = read(), y = read();
if (*st == 'Q') Q[++qcnt] = (Query){x, y, rcnt, qcnt};
else R[++rcnt] = (Change){x, y};
}
// for (int i = 1; i <= qcnt; i++) {
// printf("%d\n", Q[i].id);
// }
sort(Q + 1, Q + qcnt + 1);
int L = 1, RR = 0, last = 0;
for (int i = 1; i <= qcnt; i++) {
while (L < Q[i].x) del(col[L++]);
while (L > Q[i].x) add(col[--L]);
while (RR < Q[i].y) add(col[++RR]);
while (RR > Q[i].y) del(col[RR--]);
while (last < Q[i].tim) {
last++;
if (L <= R[last].x && R[last].x <= RR) {
add(R[last].y);
del(col[R[last].x]);
}
swap(col[R[last].x], R[last].y);
}
while (last > Q[i].tim) {
if (L <= R[last].x && R[last].x <= RR) {
add(R[last].y);
del(col[R[last].x]);
}
swap(col[R[last].x], R[last].y);
last--;
}
ans[Q[i].id] = sum;
}
for (int i = 1; i <= qcnt; i++) { write(ans[i]); printf("\n"); }
// printf("%d\n", ans[i]);
return 0;
}
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
namespace Input {
int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void write(int x) {
if (x < 10) {
putchar(x + 48);
return;
}
write(x / 10);
putchar(x % 10 + 48);
}
}using namespace Input;
const int N = 1e5 + 10;
int n, q, raw[N << 1], col[N], qcnt, ccnt, block;
struct Query{
int l, r, tim, id;
Query() :l(), r(), tim(), id(){}
Query(int _l, int _r, int _tim, int _id) :l(_l), r(_r), tim(_tim), id(_id){}
bool operator < (const Query &x) const{
if (l / block == x.l / block) {
if (r / block == x.r / block) return (r / block) & 1 ? tim < x.tim : tim > x.tim;
return r / block < x.r / block;
}
return l / block < x.l / block;
}
}Q[N];
struct Change{
int p, x;
}C[N];
void disc() {
for (int i = 1; i <= n; i++) col[i] = raw[i];
for (int i = n + 1; i <= n + ccnt; i++) raw[i] = C[i - n].x;
sort(raw + 1, raw + 1 + n + ccnt);
int cnt = unique(raw + 1, raw + 1 + n + ccnt) - raw - 1;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(raw + 1, raw + 1 + cnt, col[i]) - raw;
col[i] = pos;
}
for (int i = 1; i <= ccnt; i++) {
int pos = lower_bound(raw + 1, raw + 1 + cnt, C[i].x) - raw;
C[i].x = pos;
}
}
int cnt[N << 1], num[N << 1], ans[N];
void del(int x) {
num[cnt[x]]--;
cnt[x]--;
num[cnt[x]]++;
}
void add(int x) {
num[cnt[x]]--;
cnt[x]++;
num[cnt[x]]++;
}
int main() {
n = read(), q = read();
block = pow(n, 2.0 / 3.0);
for (int i = 1; i <= n; i++) raw[i] = read();
for (int i = 1; i <= q; i++) {
int t, x, y; t = read(), x = read(), y = read();
if (t == 1) Q[++qcnt] = Query(x, y, ccnt, qcnt);
else C[++ccnt] = (Change){x, y};
}
disc();
sort(Q + 1, Q + 1 + qcnt);
int L = 1, R = 0, last = 0;
for (int i = 1; i <= qcnt; i++) {
while (L > Q[i].l) add(col[--L]);
while (L < Q[i].l) del(col[L++]);
while (R > Q[i].r) del(col[R--]);
while (R < Q[i].r) add(col[++R]);
while (Q[i].tim > last) {
last++;
if (L <= C[last].p && C[last].p <= R) {
del(col[C[last].p]);
add(C[last].x);
}
swap(col[C[last].p], C[last].x);
}
while (Q[i].tim < last) {
if (L <= C[last].p && C[last].p <= R) {
del(col[C[last].p]);
add(C[last].x);
}
swap(col[C[last].p], C[last].x);
last--;
}
for (int j = 1; j <= R - L + 2; j++)
if (num[j] == 0) { ans[Q[i].id] = j; break; }
}
for (int i = 1; i <= qcnt; i++) { write(ans[i]); printf("\n"); }
return 0;
}
回滚莫队
回滚莫队应用于只能删点或者只能加点的情况下, 那么怎么办? 就先拿只加不减举例, 对于所有左端点处于同一块
题目
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
namespace Input{
int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void write(int x) {
if (x < 10) {
putchar(x + 48);
return;
}
write(x / 10);
putchar(x % 10 + 48);
}
}using namespace Input;
const int N = 2e5 + 10;
const int NN = 1500;
int n, m, raw[N], val[N];
void disc() {
sort(raw + 1, raw + 1 + n);
int cnt = unique(raw + 1, raw + 1 + n) - raw - 1;
for (int i = 1; i <= n; i++)
val[i] = lower_bound(raw + 1, raw + 1 + cnt, val[i]) - raw;
}
namespace Block{
int LL[NN], RR[NN], belong[N], tot, block;
void build() {
block = max(1, (int)(n * 1.0 / (sqrt(m) * 1.0)));
tot = n / block;
for (int i = 1; i <= tot; i++) {
LL[i] = (i - 1) * block + 1;
RR[i] = i * block;
}
if (RR[tot] < n) tot++, LL[tot] = RR[tot - 1] + 1, RR[tot] = n;
for (int i = 1; i <= tot; i++)
for (int j = LL[i]; j <= RR[i]; j++)
belong[j] = i;
}
}using namespace Block;
struct Query{
int l, r, id;
bool operator < (const Query &x) const{
if (belong[l] == belong[x.l]) return r < x.r;
return belong[l] < belong[x.l];
}
}Q[N];
int lt[N], rt[N], _lt[N], _rt[N], ans[N], clear[N], ccnt, cl1[N], cnt1, cl2[N], cnt2, a[N], b[N];
int main() {
// freopen("P5906_2.in", "r", stdin);
// freopen("1.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++) val[i] = raw[i] = read();
disc();
m = read();
build();
for (int i = 1; i <= m; i++)
Q[i].l = read(), Q[i].r = read(), Q[i].id = i;
sort(Q + 1, Q + 1 + m);
int L = 1, R = 0, lst_block = 0, _L, Ans = 0;
for (int i = 1; i <= m; i++) {
int l = Q[i].l, r = Q[i].r;
if (belong[l] == belong[r]) {
for (int j = l; j <= r; j++) {
if (!_lt[val[j]]) _lt[val[j]] = j, clear[++ccnt] = val[j];
else ans[Q[i].id] = max(ans[Q[i].id], j - _lt[val[j]]);
}
for (int j = 1; j <= ccnt; j++) _lt[clear[j]] = 0;
ccnt = 0;
continue;
}
if (belong[l] != lst_block) {
for (int j = 1; j <= cnt1; j++) lt[cl1[j]] = 0;
for (int j = 1; j <= cnt2; j++) rt[cl2[j]] = 0;
_L = L = RR[belong[l]] + 1, R = RR[belong[l]], Ans = cnt1 = cnt2 = 0;
lst_block = belong[l];
}
while (R < r) {
R++;
if (!lt[val[R]]) lt[val[R]] = R, cl1[++cnt1] = val[R];
rt[val[R]] = R, cl2[++cnt2] = val[R];
Ans = max(Ans, rt[val[R]] - lt[val[R]]);
}
int tmp = Ans;
while (L > l) {
L--;
if (!_rt[val[L]]) _rt[val[L]] = L, clear[++ccnt] = val[L];
tmp = max(tmp, max(rt[val[L]], _rt[val[L]]) - L);
}
ans[Q[i].id] = tmp;
L = _L;
for (int j = 1; j <= ccnt; j++) _rt[clear[j]] = 0;
ccnt = 0;
}
for (int i = 1; i <= m; i++) { write(ans[i]); printf("\n"); }
return 0;
}
//349 61 445
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
namespace Input{
int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
void write(long long x) {
if (x < 10) {
putchar(x + 48);
return;
}
write(x / 10);
putchar(x % 10 + 48);
}
}using namespace Input;
const int N = 2e5 + 10;
const int NN = 1000;
int n, q, raw[N], val[N], block;
void disc() {
for (int i = 1; i <= n; i++) val[i] = raw[i];
sort(raw + 1, raw + 1 + n);
int cnt = unique(raw + 1, raw + 1 + n) - raw - 1;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(raw + 1, raw + 1 + cnt, val[i]) - raw;
val[i] = pos;
}
}
namespace Block{
int LL[NN], RR[NN], belong[N], tot;
void build() {
tot = n / block;
for (int i = 1; i <= tot; i++) {
LL[i] = (i - 1) * block + 1;
RR[i] = i * block;
}
if (RR[tot] < n) tot++, LL[tot] = RR[tot - 1] + 1, RR[tot] = n;
for (int i = 1; i <= tot; i++)
for (int j = LL[i]; j <= RR[i]; j++)
belong[j] = i;
}
}using namespace Block;
struct Query{
int l, r, id;
bool operator < (const Query &x) const{
if (belong[l] == belong[x.l]) return r < x.r;
return belong[l] < belong[x.l];
}
}Q[N];
int cnt[N], _cnt[N];
long long ans[N], Ans;
void add(int x, long long &y) {
cnt[x]++;
y = max(y, 1ll * cnt[x] * raw[x]);
}
void del(int x) { cnt[x]--; }
int main() {
n = read(), q = read();
// printf("%d", n);
block = n * 1.0 / (sqrt(q) * 1.0);
for (int i = 1; i <= n; i++) raw[i] = read();
disc(); build();
for (int i = 1; i <= q; i++) {
int l, r; l = read(), r = read();
Q[i] = (Query){l, r, i};
}
sort(Q + 1, Q + 1 + q);
int L = 1, R = 0, _L = 1, lst_block = 0;
for (int i = 1; i <= q; i++) {
int l = Q[i].l, r = Q[i].r;
if (belong[l] == belong[r]) {
for (int j = l; j <= r; j++) _cnt[val[j]]++;
for (int j = l; j <= r; j++)
ans[Q[i].id] = max(ans[Q[i].id], 1ll * _cnt[val[j]] * raw[val[j]]);
for (int j = l; j <= r; j++) _cnt[val[j]]--;
continue;
}
if (belong[l] != lst_block) {
while (R > RR[belong[l]]) del(val[R--]);
while (L < RR[belong[l]] + 1) del(val[L++]);
Ans = 0, _L = RR[belong[l]] + 1;
lst_block = belong[l];
}
while (R < r) add(val[++R], Ans);
long long tmp = Ans;
while (L > l) add(val[--L], tmp);
ans[Q[i].id] = tmp;
while (L < _L) del(val[L++]);
}
for (int i = 1; i <= q; i++) { write(ans[i]); printf("\n"); }
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App