莫队?美队?暴力美学的经典诠释(两个都是
莫队是什么
呃……似乎没有找到「小Z的袜子命题报告」那一篇论文。
g1n0st:https://zhuanlan.zhihu.com/p/25017840
xsamsara:https://blog.csdn.net/qq_41357771/article/details/80470795
普通莫队
莫队用于处理一类区间问题:已知$[l,r]$就能够快速得到$[l+1,r]$,$[l,r-1]$此类区间的信息。
这种问题有一种最基础的暴力:
1 for (int i=1; i<=m; i++) 2 { 3 while (lt < q[i].l) del(lt++); 4 while (lt > q[i].l) add(--lt); 5 while (rt < q[i].r) add(++rt); 6 while (rt > q[i].r) del(rt--); 7 q[i].ans = ans; 8 }
类似于毛毛虫的思路扩张。
但是如果遇到$[1,n]$,$[mid,mid]$,$[1,n]$……的查询显然就被浪费了很多时间。
那么我们很自然地想到离线处理,把询问按照第一关键字l,第二关键字r的顺序排序。
然而光光这样是不够的,比如说$[1,100]$,$[1,200]$,$[2,3]$,$[2,100]$……的数据就可以卡爆单纯的排序。
同样是暴力的思想,同出一门的分块此时可以助莫队一臂之力。
我们可以这样:当l处在同一块时,按照r排序,否则按照l排序。
复杂度基本可以保证在$O(n\sqrtn)$
带修改莫队
带修莫队其实就是莫队再加一维时间轴,换句话说就是把修改操作也按照莫队的思想处理。
把修改操作也按照莫队的思想处理!
嗯,这就是我学会带修莫队后对它的概括。
莫队例题
「普通莫队」1878: [SDOI2009]HH的项链
Description
Input
Output
M行,每行一个整数,依次表示询问对应的答案。
题目分析
正解是树状数组,不过可以作为莫队的板子题。
1 #include<bits/stdc++.h> 2 const int maxn = 50003; 3 const int maxm = 200035; 4 const int maxNum = 1000035; 5 6 struct node 7 { 8 int l,r,x,ans,id; 9 }q[maxm]; 10 int a[maxn],hsh[maxNum],ans; 11 int n,m,size; 12 13 bool cmp(node a, node b){return (a.l/size < b.l/size)||(a.l/size == b.l/size&&a.r < b.r);} 14 bool cmpid(node a, node b){return a.id < b.id;} 15 int read() 16 { 17 int num = 0; 18 char ch = getchar(); 19 bool fl = 0; 20 for (; !isdigit(ch); ch = getchar()) 21 if (ch=='-') fl = 1; 22 for (; isdigit(ch); ch = getchar()) 23 num = (num<<1)+(num<<3)+ch-48; 24 if (fl) num = -num; 25 return num; 26 } 27 void add(int x){if (!(hsh[a[x]]++)) ans++;} 28 void del(int x){if (!(--hsh[a[x]])) ans--;} 29 int main() 30 { 31 n = read(), size = (int)sqrt(n); 32 for (int i=1; i<=n; i++) a[i] = read(); 33 m = read(); 34 for (int i=1; i<=m; i++) 35 q[i].l = read(), q[i].r = read(), q[i].id = i; 36 std::sort(q+1, q+m+1, cmp); 37 int lt = 1, rt = 0; 38 for (int i=1; i<=m; i++) 39 { 40 while (lt < q[i].l) del(lt++); 41 while (lt > q[i].l) add(--lt); 42 while (rt < q[i].r) add(++rt); 43 while (rt > q[i].r) del(rt--); 44 q[i].ans = ans; 45 } 46 std::sort(q+1, q+m+1, cmpid); 47 for (int i=1; i<=m; i++) printf("%d\n",q[i].ans); 48 return 0; 49 }
「带修莫队」2120: 数颜色
Description
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?
Input
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
Output
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
HINT
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
题目分析
就是上面的分析,
直接挂代码吧
1 #include<bits/stdc++.h> 2 const int maxn = 50035; 3 const int maxNum = 1000035; 4 5 int size; 6 struct CHANGEs 7 { 8 int x,y,lst; 9 CHANGEs() {} 10 CHANGEs(int a, int b, int c):x(a),y(b),lst(c) {} 11 }u[maxn]; 12 struct QRs 13 { 14 int l,r,tim,id; 15 bool operator < (QRs a) const 16 { 17 if (l/size==a.l/size){ 18 if (r/size==a.r/size){ 19 return tim < a.tim; 20 }else return r/size < a.r/size; 21 }else return l/size < a.l/size; 22 } 23 QRs() {} 24 QRs(int a, int b, int c, int d):l(a),r(b),tim(c),id(d) {} 25 }q[maxn]; 26 int n,m,cntu,cntq,L,R,now,tot; 27 int a[maxn],lst[maxn],ans[maxn],hsh[maxNum]; 28 char ch[13]; 29 30 int read() 31 { 32 int num = 0; 33 char ch = getchar(); 34 bool fl = 0; 35 for (; !isdigit(ch); ch = getchar()) 36 if (ch=='-') fl = 1; 37 for (; isdigit(ch); ch = getchar()) 38 num = (num<<1)+(num<<3)+ch-48; 39 if (fl) num = -num; 40 return num; 41 } 42 void change(int x, int col) 43 { 44 if (x >= L&&x <= R){ 45 if (!(--hsh[a[x]])) tot--; 46 a[x] = col; 47 if (!(hsh[a[x]]++)) tot++; 48 }else a[x] = col; 49 } 50 void add(int x){if (!(hsh[x]++)) tot++;} 51 void del(int x){if (!(--hsh[x])) tot--;} 52 int main() 53 { 54 n = read(), m = read(); 55 size = pow(n, 2.0/3); 56 for (int i=1; i<=n; i++) lst[i] = a[i] = read(); 57 for (int i=1; i<=m; i++) 58 { 59 scanf("%s",ch); 60 int l = read(), r = read(); 61 if (ch[0]=='Q') 62 q[++cntq] = QRs(l, r, cntu, cntq); 63 else{ 64 u[++cntu] = CHANGEs(l, r, lst[l]); 65 lst[l] = r; 66 } 67 } 68 std::sort(q+1, q+cntq+1); 69 L = 1, R = 0, now = 0; 70 for (int i=1; i<=cntq; i++) 71 { 72 while (q[i].tim < now) change(u[now].x, u[now].lst),now--; 73 while (q[i].tim > now) now++,change(u[now].x, u[now].y); 74 while (L < q[i].l) del(a[L++]); 75 while (L > q[i].l) add(a[--L]); 76 while (R < q[i].r) add(a[++R]); 77 while (R > q[i].r) del(a[R--]); 78 ans[q[i].id] = tot; 79 } 80 for (int i=1; i<=cntq; i++) printf("%d\n",ans[i]); 81 return 0; 82 }
「在更」