莫队其实就是一个带优化的暴力,通过将区间询问按一定规则进行排序,从而优化过程,求出答案。
举一例子:(例子不具备权威性,只是让读者了解莫队是干啥的)
/* 输入四个区间 1 4 初始条件,L= R = 0, 将R遍历到4 需要走4步 L走一步,共5次 4 8 继承上一次 L 和 R 的值,L从1到4 需要3次,R从4到8,需4次, 总计8次 2 9 同理继承, L回退2次, R前进一次 总计3次 1 2 同理,L回退1次,R回退7次 总计8次 如果直接暴力,计算机将要计算 5+8+3+8=24次 如果将询问进行排序呢?假设排列成如下模样: 1 2 总走3次 1 4 总走2次 2 9 总走6次 4 8 总走3次 全局总走3+2+6+3=14次,这相差是不是很大?这也是莫队的思想,将暴力优化。 */
至于如何将询问排序,证明就有一点烦恼,直接说结论:
:
可以借助分块思想,假设总区间为n,那么每一块区间长度应趋近于 n^(2.0/3)
可以将询问中的 L或者是 R,按其所在块的编号, 从小到大排序,然后再慢慢暴力。。。这就是莫队,说一下莫队的数据极限是 5e5,超过1e7,则需要另谋高就。
附上一题:P1494 [国家集训队]小Z的袜子
对于该题,有着优化操作,也是组合公式的直接约分简化。
方案1:
方案2:
#include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<cstdlib> #include<cmath> #include<queue> #include<stack> #include<map> #include<algorithm> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) #define Mem0(x) memset(x,0,sizeof(x)) #define Mem1(x) memset(x,-1,sizeof(x)) #define MemX(x) memset(x,0x3f,sizeof(x)) using namespace std; typedef long long ll; const int inf=0x3f3f3f; const double pi=acos(-1.0); const int N=500010; int B,ans[N][5],fz,fm,len,n,m,col[N],cnt[N]; struct s{ int l,r,id; bool operator < (s&b) { return l/B==b.l/B?r<b.r:l<b.l; } }q[N]; void init() { len=fz=fm=0; memset(cnt,0,sizeof(cnt)); cin>>n>>m; B=sqrt(n); for (int i=1;i<=n;i++){ cin>>col[i]; } for (int i=0;i<m;i++){ cin>>q[i].l>>q[i].r; q[i].id=i; } sort(q,q+m); } bool cmp(s&a,s&b) { if (a.l/B==b.l/B) return a.l>b.l; } void add(int x) { fz+=cnt[x]; cnt[x]++; fm+=len; len++; } void delect(int x) { cnt[x]--; fz-=cnt[x]; len--; fm-=len; } int main() { init(); int l=1,r=0; for (int i=0;i<m;i++){ if (q[i].l==q[i].r){ ans[q[i].id][0]=0; ans[q[i].id][1]=1; continue; } while (l<q[i].l){ delect(col[l++]); } while (l>q[i].l){ add(col[--l]); } while (r<q[i].r){ add(col[++r]); } while (r>q[i].r){ delect(col[r--]); } int tmp=__gcd(fz,fm); ans[q[i].id][0]=fz/tmp; ans[q[i].id][1]=fm/tmp; } for (int i=0;i<m;i++){ printf("%d/%d ",ans[i][0],ans[i][1]); } return 0; }
带修莫队就是带上了修改。。。。 (待更新)
*****************************************更新******************************************
简单的来说:
带修莫队 = 莫队 + 时间轴
对于原来莫队,就是先对 询问 L按所在分块编号从小到大排序,当相等的时候则按 R 所在分块的从大到小排序。
至于带修莫队,就是在原莫队上再加一轴(时间轴),先将l分块,再讲r分块,同一块的按 t 排序。
排好序之后,查询遍历,如果当前修改次数 比 本次查询改的多,则改回去,反之则再次修改,直到修改次数与本次查询改的次数相同。