简单莫队
莫队
正常莫队时间(左端点+右端点)复杂度:
当\(S=\frac{n}{\sqrt m}\)时取到最小
带修莫队时间复杂度:
大约在\(S=n^{\frac{2}{3}}\)时取到最小值
例题1
考虑如何快速求出\([l,r]\)的商
不妨\(s[r]=\sum_{i=1}^{r}10^{r-i}\times a[i]\)
则显然\([l,r]\)的答案可以表示为\(s[r]-s[l-1]\times 10^{r-l+1}\)(比较好做,毕竟相同的十足求相同的就行,如果另外一种方式就需要两个不同的数组找相同的元素)
则:
可以变成:
显然需要逆元,而逆元存在的条件是\(gcd(10,p)=1\),所以需要特判p=2,p=5
然后统计\([l,r+1]\)中相同大小的数的对数,显然需要莫队
而特判只需要末尾是偶数或者0,5即可
本来可以直接预处理求出位置和个数的前缀和然后\(O(n)\)解决
但我傻逼了,也写了莫队
WA了一次,没画图
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,m,p,s[N],bl[N],S;
char a[N];
ll Ans[N];
struct A{
int l,r,id;
}Q[N];
inline bool cmp(A i,A j) {
return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r;
}
namespace www {
ll ans; int sum;
inline void add(int i) {
if(!((a[i]-'0')&1)) sum++,ans+=i;
}
inline void dec(int i) {
if(!((a[i]-'0')&1)) sum--,ans-=i;
}
void main() {
for(int i=1;i<=m;i++) {
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+m+1,cmp);
int ql=1,qr=0;
for(int i=1;i<=m;i++) {
while(Q[i].l<ql) ql--,add(ql);
while(Q[i].l>ql) dec(ql),ql++;
while(Q[i].r>qr) qr++,add(qr);
while(Q[i].r<qr) dec(qr),qr--;
Ans[Q[i].id]=ans-(ll)sum*(Q[i].l-1);
}
for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
}
}
namespace sss {
ll ans; int sum;
inline void add(int i) {
if((a[i]-'0')%5==0) sum++,ans+=i;
}
inline void dec(int i) {
if((a[i]-'0')%5==0) sum--,ans-=i;
}
void main() {
for(int i=1;i<=m;i++) {
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+m+1,cmp);
int ql=1,qr=0;
for(int i=1;i<=m;i++) {
while(Q[i].l<ql) ql--,add(ql);
while(Q[i].l>ql) dec(ql),ql++;
while(Q[i].r>qr) qr++,add(qr);
while(Q[i].r<qr) dec(qr),qr--;
Ans[Q[i].id]=ans-(ll)sum*(Q[i].l-1);
}
for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
}
}
namespace fff {
ll ans;
int t[N],b[N];
inline void add(int i) {
ans+=t[s[i]];
t[s[i]]++;
}
inline void dec(int i) {
t[s[i]]--;
ans-=t[s[i]];
}
inline bool cmp2(int i,int j) {
return s[i]<s[j];
}
inline int ksm(ll a,int b) {
ll ret=1;
while(b){
if(b&1) ret=ret*a%p;
a=a*a%p,b>>=1;
}
return ret;
}
void main() {
for(int i=1;i<=n;i++) {
s[i]=((ll)s[i-1]*10+a[i]-'0')%p;
}
int t=1,fm=ksm(10,p-2);
for(int i=1;i<=n;i++) {
t=(ll)t*fm%p;
s[i]=(ll)s[i]*t%p;
b[i]=i;
}
sort(b,b+n+1,cmp2);
t=1; int lst=s[b[0]]; s[b[0]]=t;
for(int i=1;i<=n;i++) {
if(s[b[i]]!=lst) ++t;
lst=s[b[i]]; s[b[i]]=t;
}
for(int i=1;i<=m;i++) {
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].l--;
Q[i].id=i;
}
sort(Q+1,Q+m+1,cmp);
int ql=1,qr=0;
for(int i=1;i<=m;i++) {
while(Q[i].l<ql) ql--,add(ql);
while(Q[i].l>ql) dec(ql),ql++;
while(Q[i].r>qr) qr++,add(qr);
while(Q[i].r<qr) dec(qr),qr--;
Ans[Q[i].id]=ans;
}
for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
}
}
int main() {
scanf("%d%s%d",&p,a+1,&m);
n=strlen(a+1),S=sqrt(n);
for(int i=1;i<=n;i++) bl[i]=(i-1)/S+1;
if(p==2) www::main();
else if(p==5) sss::main();
else fff::main();
return 0;
}
树上莫队
需要划分块,也就是把树分成了若干块,使得点之间的最大距离在一个较小的范围内
一般我选择DFS分块,就是做完以后将点加入队列,某个点的子树做完后队列中的元素个数超过S,将它们分到一个块中,据说块的最大大小是\([S,3S)\)
然后就是暴力地从\((u_1,v_1)\)转移到\((u_2,v_2)\),可以通过异或实现,异或表示是否被计算
可以暴力从\(u_1->u_2,v_1->v_2\)
在转移时不要算他们的LCA,因为往往LCA也在其中(红点需要取反)
但需要更正\((u_1,v_1)\),\((u_2,v_2)\) 的LCA,也就是对它们的LCA取反
可以发现\((u1,v1),(u2,v2)\)还需要更正1次
而且因为异或的性质不需要特判,每次都做即可
总结一下:
-
DFS划分块
-
移动的时候不要计算LCA
-
计算两组的LCA
模板
和其他莫队一样,树上莫队也有自己的写法
Q[0].l=Q[0].r=rt; //未计算lca(rt),后面也就没必要再计算0的lca
while() {
move...
int l=lca(Q[i].l,Q[i].r)
upd(lca)
give_ans...
upd(lca)//计算两组的lca(先计算完了)
}
TLE了一次:分块直接在进入队列后就分了,应该是在做完子树后再分
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,m,q,S,bl[N],dep[N],f[N],t[M],st[N],top,a[N],b[N],c[N],col;
ll ans,Ans[N];
bool s[N];
vector<int>V[N];
struct A{ int x,y; }d[N];
struct B{int id,l,r,i; }Q[N];
void fk(int fa,int u) {
dep[u]=dep[fa]+1,f[u]=fa;
int lst=top;
for(int v:V[u]) {
if(v!=fa) {
fk(u,v);
if(top-lst>=S) {
++col;
for(int i=lst+1;i<=top;i++) {
bl[st[i]]=col;
}
top=lst+1;
}
}
}
st[++top]=u;
}
inline bool cmp(B i,B j) {
return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&bl[i.r]<bl[j.r]||bl[i.l]==bl[j.l]&&bl[i.r]==bl[j.r]&&i.i<j.i;
}
inline void upd(int u) {
s[u]^=1;
if(s[u]==1) {
t[c[u]]++;
ans+=(ll)b[t[c[u]]]*a[c[u]];
} else {
ans-=(ll)b[t[c[u]]]*a[c[u]];
t[c[u]]--;
}
}
void Move(int u,int v) {
while(u!=v) {
if(dep[u]>dep[v]) swap(u,v);
upd(v);
v=f[v];
}
}
namespace LCA {
int dep[N],f[N][20],lg[N];
int lca(int u,int v) {
if(dep[u]>dep[v]) swap(u,v);
while(dep[u]<dep[v]) v=f[v][lg[dep[v]-dep[u]]];
if(u==v) return u;
for(int i=lg[dep[u]];i>=0;i--) {
if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
}
return f[u][0];
}
void dfs(int fa,int u) {
dep[u]=!fa?0:dep[fa]+1,f[u][0]=fa;
for(int i=1;i<=lg[dep[u]];i++) {
f[u][i]=f[f[u][i-1]][i-1];
}
for(int v:V[u]) {
if(v!=fa) {
dfs(u,v);
}
}
}
inline void init() {
lg[0]=-1;
for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
dfs(0,1);
}
}
int main() {
scanf("%d%d%d",&n,&m,&q); S=pow(n,0.667);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<n;i++) {
int u,v; scanf("%d%d",&u,&v);
V[u].push_back(v),V[v].push_back(u);
}
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
LCA::init();
fk(0,1);
if(top) {
++col;
for(int i=1;i<=top;i++) bl[st[i]]=col;
top=0;
}
m=0; int cnt=0;
for(int i=1;i<=q;i++) {
int op,x,y; scanf("%d%d%d",&op,&x,&y);
if(!op) d[++cnt]=(A){x,y};
else {
m++; Q[m]=(B){m,x,y,cnt};
}
}
sort(Q+1,Q+m+1,cmp);
Q[0].l=Q[0].r=1; int qi=0;
for(int i=1;i<=m;i++) {
Move(Q[i-1].l,Q[i].l),Move(Q[i-1].r,Q[i].r);
int l=LCA::lca(Q[i].l,Q[i].r);
upd(l);
while(qi<Q[i].i){
qi++; int u=d[qi].x;
if(s[u]) {
ans-=(ll)b[t[c[u]]]*a[c[u]];
t[c[u]]--;
swap(c[u],d[qi].y);
t[c[u]]++;
ans+=(ll)b[t[c[u]]]*a[c[u]];
} else swap(c[u],d[qi].y);
}
while(qi>Q[i].i) {
int u=d[qi].x;
if(s[u]) {
ans-=(ll)b[t[c[u]]]*a[c[u]];
t[c[u]]--;
swap(c[u],d[qi].y);
t[c[u]]++;
ans+=(ll)b[t[c[u]]]*a[c[u]];
} else swap(c[u],d[qi].y);
qi--;
}
Ans[Q[i].id]=ans;
upd(l);
}
for(int i=1;i<=m;i++) {
printf("%lld\n",Ans[i]);
}
return 0;
}
模板2
TLE了一次:错在排序是第一关键字是bl,第二关键字不再是bl而用了它自己(数列习惯了)
因为树上它自己屁用也莫得,所以两个关键字都应该是bl(修改可以是自己)
#include<bits/stdc++.h>
using namespace std;
inline int rd() {
int ret=0; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
return ret;
}
const int N=1e5+5;
int n,m,q,S,bl[N],dep[N],f[N],t[N],st[N],top,c[N],col,ans,Ans[N];
bool s[N];
vector<int>V[N];
struct A{ int id,l,r,a,b; }Q[N];
void fk(int fa,int u) {
dep[u]=dep[fa]+1,f[u]=fa;
int lst=top;
for(int v:V[u]) {
if(v!=fa) {
fk(u,v);
if(top-lst>=S) {
++col;
for(int i=lst+1;i<=top;i++) bl[st[i]]=col;
top=lst;
}
}
}
st[++top]=u;
}
inline bool cmp(A i,A j) {
return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&bl[i.r]<bl[j.r];
}
inline void upd(int u) {
s[u]^=1;
if(s[u]==1) {
t[c[u]]++;
if(t[c[u]]==1)ans++;
} else {
if(t[c[u]]==1) ans--;
t[c[u]]--;
}
}
void Move(int u,int v) {
while(u!=v) {
if(dep[u]>dep[v]) swap(u,v);
upd(v);
v=f[v];
}
}
namespace LCA {
int dep[N],f[N][20],lg[N];
int lca(int u,int v) {
if(dep[u]>dep[v]) swap(u,v);
while(dep[u]<dep[v]) v=f[v][lg[dep[v]-dep[u]]];
if(u==v) return u;
for(int i=lg[dep[u]];i>=0;i--) {
if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
}
return f[u][0];
}
void dfs(int fa,int u) {
dep[u]=!fa?0:dep[fa]+1,f[u][0]=fa;
for(int i=1;i<=lg[dep[u]];i++) {
f[u][i]=f[f[u][i-1]][i-1];
}
for(int v:V[u]) {
if(v!=fa) {
dfs(u,v);
}
}
}
inline void init() {
lg[0]=-1;
for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
dfs(0,1);
}
}
int main() {
n=rd(),m=rd(),S=sqrt(n);
for(int i=1;i<=n;i++) c[i]=rd();
for(int i=1;i<=n;i++) {
int u=rd(),v=rd();
V[u].push_back(v),V[v].push_back(u);
}
LCA::init();
fk(0,1);
if(top) {
++col;
for(int i=1;i<=top;i++) bl[st[i]]=col;
top=0;
}
for(int i=1;i<=m;i++) {
Q[i]=(A){i,rd(),rd(),rd(),rd()};
if(bl[Q[i].l]>bl[Q[i].r]) swap(Q[i].l,Q[i].r);
}
sort(Q+1,Q+m+1,cmp);
Q[0].l=Q[0].r=1;
for(int i=1;i<=m;i++) {
Move(Q[i-1].l,Q[i].l),Move(Q[i-1].r,Q[i].r);
int l=LCA::lca(Q[i].l,Q[i].r);
upd(l);
Ans[Q[i].id]=ans;
if(Q[i].a!=Q[i].b&&t[Q[i].a]&&t[Q[i].b]) {
Ans[Q[i].id]--;
}
upd(l);
}
for(int i=1;i<=m;i++) {
printf("%d\n",Ans[i]);
}
return 0;
}
回滚莫队
用于解决不能加或者不能删的题目
自行百度
例题0
LuoguP5906 【模板】回滚莫队&不删除莫队
Wa了一次:在暴力做时忘记还原ret了
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2e5+5;
int n,S,m,fid[N],a[N],bl[N],t[N],tt[N],ans[N];
struct B{int *l; int r; };
vector<B>V;
struct A{int l,r,id; }q[N];
bool cmp(A i,A j) {
return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d",&n); S=sqrt(n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]),fid[i]=a[i];
bl[i]=(i-1)/S+1;
}
sort(fid+1,fid+n+1);
int cnt=unique(fid+1,fid+n+1)-fid-1;
for(int i=1;i<=n;i++) {
a[i]=lower_bound(fid+1,fid+cnt+1,a[i])-fid;
}
scanf("%d",&m);
for(int i=1;i<=m;i++) {
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int fro=0,r=0,ret=0;
for(int i=1;i<=m;i++) {
if(i==1||bl[q[i].l]!=fro) {
fro=bl[q[i].l];
r=fro*S+1,ret=0;
memset(t,0,sizeof(t));
memset(tt,0,sizeof(tt));
}
if(fro==bl[q[i].r]) {
for(int j=q[i].l;j<=q[i].r;j++) {
if(!t[a[j]]) t[a[j]]=j;
else ret=max(ret,j-t[a[j]]);
}
ans[q[i].id]=ret; ret=0;
for(int j=q[i].l;j<=q[i].r;j++) {
t[a[j]]=0;
}
continue;
}
for(;r<=q[i].r;r++) {
tt[a[r]]=r;
if(!t[a[r]]) t[a[r]]=r;
ret=max(ret,tt[a[r]]-t[a[r]]);
}
V.clear(); V.pb((B){&ret,ret});
for(int j=fro*S;j>=q[i].l;j--){
V.pb((B){&t[a[j]],t[a[j]]});
t[a[j]]=j;
if(!tt[a[j]]) {
V.pb((B){&tt[a[j]],tt[a[j]]});
tt[a[j]]=j;
}
ret=max(ret,tt[a[j]]-t[a[j]]);
}
ans[q[i].id]=ret;
for(int j=V.size()-1;j>=0;j--) {
*V[j].l=V[j].r;
}
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
例题1
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5;
int n,m,q,a[N],b[N],fid[N],S,bl[N],t[N];
ll ans,Ans[N],tmp;
struct A{int l,r,id; }Q[N];
inline bool cmp(int i,int j) {
return a[i]<a[j];
}
inline bool cmp2(A i,A j) {
return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r;
}
int main() {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=i;
sort(b+1,b+n+1,cmp);
int m=1; fid[1]=a[b[1]],a[b[1]]=m;
for(int i=2;i<=n;i++) {
if(a[b[i]]!=fid[a[b[i-1]]]) {
fid[++m]=a[b[i]];
}
a[b[i]]=m;
}
S=sqrt(n);
for(int i=1;i<=n;i++) bl[i]=(i-1)/S+1;
for(int i=1;i<=q;i++) {
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i;
}
sort(Q+1,Q+q+1,cmp2);
for(int i=1;i<=q;) {
memset(t,0,sizeof(t)),ans=0;
for(;i<=q&&bl[Q[i].l]==bl[Q[i].r];i++) {
for(int k=Q[i].l;k<=Q[i].r;k++) {
t[a[k]]++;
ans=max(ans,(ll)fid[a[k]]*t[a[k]]);
}
Ans[Q[i].id]=ans; ans=0;
for(int k=Q[i].l;k<=Q[i].r;k++) {
t[a[k]]--;
}
}
for(int j=bl[Q[i].l]*S+1;j<=Q[i].r;j++) {
t[a[j]]++;
ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
}
tmp=ans;
for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
t[a[j]]++;
ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
}
Ans[Q[i].id]=ans,ans=tmp;
for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
t[a[j]]--;
}
for(i++;i<=q&&bl[Q[i].l]==bl[Q[i-1].l];i++) {
for(int j=Q[i-1].r+1;j<=Q[i].r;j++) {
t[a[j]]++;
ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
}
tmp=ans;
for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
t[a[j]]++;
ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
}
Ans[Q[i].id]=ans,ans=tmp;
for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
t[a[j]]--;
}
}
}
for(int i=1;i<=q;i++) {
printf("%lld\n",Ans[i]);
}
return 0;
}
例题2
ybtoj A. 值域连续
考虑莫队,需要删除,套线段树,记录连续最大,左边最大和右边最大,显然TLE
所以考虑回滚莫队,只需要加点和撤销
又因为这是一个排列,不重
所以只需要在每段连续数字的开头结尾维护答案,中间不需要处理
故\(L[i]\)表示向左的最大长度,\(R[i]\)表示向右的最大长度即可
当然也可以用并查集维护,每次路径压缩时把所有改的节点记录到栈中
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,m,S,L[N],R[N],a[N],ans[N],bl[N],top,Ans;
struct A{int l,r,i; }q[N],st[N];
bool cmp(A i,A j) {
return bl[i.l]==bl[j.l]?i.r<j.r:i.l<j.l;
}
inline void add(int x) {
L[x]=L[x-1]+1,R[x]=R[x+1]+1;
R[x-L[x]+1]=R[x]+L[x]-1;
L[x+R[x]-1]=R[x]+L[x]-1;
Ans=max(Ans,R[x]+L[x]-1);
}
inline void Add(int x,int i) {
st[++top]=(A){L[x],R[x],x};
L[x]=L[x-1]+1,R[x]=R[x+1]+1;
st[++top]=(A){L[x-L[x]+1],R[x-L[x]+1],x-L[x]+1};
R[x-L[x]+1]=R[x]+L[x]-1;
st[++top]=(A){L[x+R[x]-1],R[x+R[x]-1],x+R[x]-1};
L[x+R[x]-1]=R[x]+L[x]-1;
ans[i]=max(ans[i],R[x]+L[x]-1);
}
int main() {
freopen("permu.in","r",stdin);
freopen("permu.out","w",stdout);
scanf("%d%d",&n,&m); S=sqrt(n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
bl[i]=(i-1)/S+1;
}
int c=0;
for(int i=1;i<=m;i++) {
int l,r; scanf("%d%d",&l,&r);
if(bl[l]==bl[r]) {
Ans=0;
for(int j=l;j<=r;j++) {
add(a[j]);
}
ans[i]=Ans;
for(int j=l;j<=r;j++) {
L[a[j]]=R[a[j]]=0;
}
} else {
q[++c]=(A){l,r,i};
}
}
sort(q+1,q+c+1,cmp);
int r=0;
for(int i=1;i<=c;i++) {
if(bl[q[i-1].l]!=bl[q[i].l]) {
r=bl[q[i].l]*S;
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
Ans=0;
}
while(r<q[i].r) r++,add(a[r]);
top=0; ans[q[i].i]=Ans;
for(int j=bl[q[i].l]*S;j>=q[i].l;j--) {
Add(a[j],q[i].i);
}
while(top) {
L[st[top].i]=st[top].l;
R[st[top].i]=st[top].r;
top--;
}
}
for(int i=1;i<=m;i++) {
printf("%d\n",ans[i]);
}
return 0;
}