目录
莫队
前言
完了我发现什么都不会啊w(゚Д゚)w
参考博文
【莫队算法】-大米饼
上面这篇博文写的很好 尤其是复杂度的证明这一块
很精辟
正文
- 莫队是啥
暴力的优化
通过排序来减少枚举的复杂度 - 什么决定了莫队的复杂度
几个指针的跳动 - 莫队可以解决什么问题
- 需要离线解决问题
- 用于区间问题 且 能以极少的时间向左右两边转换(一般为\(O(1)\)
- 对于修改与不修改没什么要求
但莫队的思想美妙,代码优美,你值得拥有。莫队的排序思想也为众多离线处理的题目提供了完整的思路。
入门题:
两个指针的跳动bzoj2038|2009国家集训队|小Z的袜子
给你一个颜色序列 每次询问\([l,r]\)区间里随便选两个是相同颜色的概率
设\(num[i]\)表示颜色为\(i\)的袜子数量区间长度为\(len\) 显然答案为\(\frac{\sum_{i=1}^{n}num[i]^2-num[i]}{len*len-len}\)
如果你知道\([l,r]\)的答案相信你一定能\(O(1)\)求出\([l,r+1]\)的答案
两个指针的跳动 最好的块分的大小为\(\sqrt{n}\) 时间复杂度为\(n^{\frac{3}{2}}\) 下面给出证明TAT
设块的大小为\(sz\) 对询问排序的时候 如果\(l\)在同一个块里 \(r\)排序 不在一个块里按\(l\)排序
那么对于一个块
- \(r\)指针的跳动次数最坏为\(n\) 所以总的跳动次数为\(n*\frac{n}{unit}\)
- \(l\)指针 注意\(l\)在一个块中是无序的 所以在一个块中一次\(l\)的最大跨度为\(unit\) 又一个块中最多跳\(m\)次 所以\(l\)的复杂度为\(m*unit\)
\(m\)和\(n\)同级 所以总复杂度为\(n*unit+\frac{n*n}{unit}\)
可以由基本不等式得 复杂度的最小值为\(n\sqrt{n}\) 取到最小值时\(unit\)为\(\sqrt{n}\)
#include<bits/stdc++.h>
#define fr(i,x,y) for(int i=x;i<=y;++i)
#define rf(i,x,y) for(int i=x;i>=y;--i)
#define LL long long
using namespace std;
const int N=5e4+10;
struct data{
int l,r,id;
}a[N];
int A[N],B[N];
LL fz[N],fm[N],nm[N];
bool cmp(data xx,data yy){
return B[xx.l]==B[yy.l]?xx.r<yy.r:xx.l<yy.l;
}
LL gcd(LL x,LL y){
return y?gcd(y,x%y):x;
}
LL pf(LL x){
return x*x;
}
int main(){
int n,m;scanf("%d%d",&n,&m);
int sz=sqrt(n);
fr(i,1,n) scanf("%d",&A[i]),B[i]=i/sz+1;
fr(i,1,m){
a[i].id=i;
scanf("%d%d",&a[i].l,&a[i].r);
}
sort(a+1,a+1+m,cmp);
int l=1,r=0;
LL ff=0;
fr(i,1,m){
while(l<a[i].l) ff-=pf(nm[A[l]]),ff+=pf(--nm[A[l]]),l++;
while(l>a[i].l) l--,ff-=pf(nm[A[l]]),ff+=pf(++nm[A[l]]);
while(r<a[i].r) r++,ff-=pf(nm[A[r]]),ff+=pf(++nm[A[r]]);
while(r>a[i].r) ff-=pf(nm[A[r]]),ff+=pf(--nm[A[r]]),r--;
LL pos=a[i].r-a[i].l+1,sum=pos*pos;
fz[a[i].id]=ff-pos,fm[a[i].id]=sum-pos;
LL gg=gcd(ff-pos,sum-pos);
if(a[i].l==a[i].r) fz[a[i].id]=0,fm[a[i].id]=1;
else fz[a[i].id]/=gg,fm[a[i].id]/=gg;
}
fr(i,1,m) printf("%lld/%lld\n",fz[i],fm[i]);
return 0;
}
mdT了好久啊TAT!!!
三个指针的跳动bzoj2120数颜色
给你一个颜色序列 操作有两种 1. 修改x上的颜色为\(y\) 2.询问\([l,r]\)区间里一共有多少个不同的颜色
三个指针的莫队 考虑其分成多少大的块(块的最佳大小为\(n^\frac{2}{3}\) 时间复杂度为\(n^\frac{5}{3}\)) 下面给出证明:
排序的时候
bool cmp(Query xx,Query yy){
if(B[xx.l]==B[yy.l]){
if(B[xx.r]==B[yy.r]) return xx.T<yy.T;
else return xx.r<yy.r;
} else return xx.l<yy.l;
}
和上面一样分别对\(l,r,T\)进行讨论
- 对于\(l\) 同上是\(m*unit\)即\(n*unit\)
- 对于\(r\) 同上是\(\frac{n*n}{unit}\)
- 对于\(T\) 可以观察到只有\(l,r\)在同一个块里的时候才会有序
- 那么最坏的情况下 一个\(l\)块里有所有的\(r\)块
那么\(T\)的单调块就有\(\frac{n}{unit}*\frac{n}{unit}\) 即\(T\)的复杂度为\(n*\frac{n*n}{unit*unit}\)
- 那么最坏的情况下 一个\(l\)块里有所有的\(r\)块
综上总复杂度为\(O(n*unit+\frac{n*n}{unit}+n*\frac{n*n}{unit*unit})\)
是不是该去问数竞大佬这个不等式怎么搞最小值的TAT
#include<bits/stdc++.h>
#define fr(i,x,y) for(int i=x;i<=y;++i)
#define rf(i,x,y) for(int i=x;i>=y;--i)
#define LL long long
using namespace std;
const int N=1e4+10,M=1e6+10;
struct Query{ int id,l,r,T; }q[N];
struct Change{ int x,nw,od; }c[N];
int pre[N],B[N],nm[M],d[N],a[N],ans=0;
bool cmp(Query xx,Query yy){
if(B[xx.l]==B[yy.l]){
if(B[xx.r]==B[yy.r]) return xx.T<yy.T;
else return xx.r<yy.r;
} else return xx.l<yy.l;
}
void Upd(int x,int flg){
// printf("GG %d %d\n",x,flg);
if(nm[x]) ans--;
nm[x]+=flg;
if(nm[x]) ans++;
}
void Go(int sit,int x,int y,int l,int r){
// printf("gg%d %d %d %d %d\n",sit,x,y,l,r);
if(sit>=l&&sit<=r) Upd(x,-1),Upd(y,1);
a[sit]=y;
}
int main(){
int n,m;scanf("%d%d",&n,&m);
int sz=pow(n,0.666666);
fr(i,1,n) scanf("%d",&pre[i]),a[i]=pre[i],B[i]=i/sz+1;
int T=0,cnt=0;
fr(i,1,m){
char ch;cin>>ch;
int x,y;scanf("%d%d",&x,&y);
if(ch=='Q') q[++cnt]=(Query){cnt,x,y,T};
else c[++T]=(Change){x,y,pre[x]},pre[x]=y;
}
sort(q+1,q+1+cnt,cmp);
/*fr(i,1,cnt){
printf("id=%d l=%d r=%d T=%d\n",q[i].id,q[i].l,q[i].r,q[i].T);
}*/
int l=1,r=0;T=0;
fr(i,1,cnt){
while(q[i].T>T) T++,Go(c[T].x,c[T].od,c[T].nw,l,r);
while(q[i].T<T) Go(c[T].x,c[T].nw,c[T].od,l,r),T--;
while(l<q[i].l) Upd(a[l],-1),l++;
while(l>q[i].l) l--,Upd(a[l],1);
while(r<q[i].r) r++,Upd(a[r],1);
while(r>q[i].r) Upd(a[r],-1),r--;
d[q[i].id]=ans;
}
fr(i,1,cnt) printf("%d\n",d[i]);
return 0;
}
树上莫队带修改bzoj1429|Haruna’s Breakfast
- 你的预备知识
- LCA
- 莫队
- 树上分块建议尝试bzoj1086|SCOI2005王室联邦
续了我好久QwQ
树上分块+颜色块
写的太长了TAT
#include<bits/stdc++.h>
#define fr(i,x,y) for(int i=x;i<=y;++i)
#define rf(i,x,y) for(int i=x;i>=y;--i)
#define LL long long
using namespace std;
const int N=5e4+10;
struct Query{ int id,u,v,T; }q[N];
struct Change{ int x,od,nw; }c[N];
int tp=0,Q[N],col=0;
int n,m,sz,gg,bk,cnt=0,res=0;
int B[N],b[N],L[N],R[N],sum[N],nm[N],a[N],pre[N],vis[N],d[N];/*B:tree b:w*/
int eu[N<<1],to[N<<1],nt[N<<1],mk[N],st[N<<1][20],head[N],dep[N],f[N];
void add(int x,int y){
to[++cnt]=y,nt[cnt]=head[x],head[x]=cnt;
to[++cnt]=x,nt[cnt]=head[y],head[y]=cnt;
}
void dfs(int u,int fa){
f[u]=fa,dep[u]=dep[fa]+1;
eu[++res]=u;
int lt=tp;
for(int i=head[u];i;i=nt[i]){
if(to[i]==fa) continue;
dfs(to[i],u);
if(tp-lt>=sz){
++col;
while(tp!=lt) B[Q[tp--]]=col;
}
eu[++res]=u;
}
Q[++tp]=u;
}
void Gao(){
while(tp) B[Q[tp--]]=col;
memset(mk,-1,sizeof mk);
fr(i,1,res){
if(mk[eu[i]]==-1) mk[eu[i]]=i;
st[i][0]=eu[i];
}
fr(i,1,19) fr(j,1,res){
if(j+(1<<i)+1>=res) continue;
int pos=st[j][i-1],tmp=st[j+(1<<(i-1))][i-1];
if(dep[pos]<dep[tmp]) st[j][i]=pos;
else st[j][i]=tmp;
}
}
int GetLCA(int l,int r){
if(l>r) swap(l,r);
int kk=log(double(r-l+1))/log(2.0);
int pos=st[l][kk],tmp=st[r-(1<<kk)+1][kk];
if(dep[pos]<dep[tmp]) return pos;
else return tmp;
}
bool cmp(Query xx,Query yy){
if(B[xx.u]==B[yy.u]){
if(B[xx.v]==B[yy.v]) return xx.T<yy.T;
else return xx.v<yy.v;
} else return xx.u<yy.u;
}
void Gai(int sit,int x,int flg){
if(x>n) return ;
if(nm[x]) sum[b[x]]--;
nm[x]+=flg;
if(nm[x]) sum[b[x]]++;
}
void Upd(int sit,int x,int y){
if(vis[sit]){
Gai(sit,x,-1),Gai(sit,y,1);
}
a[sit]=y;
}
int Get(){
fr(i,1,gg){
if(sum[i]==R[i]-L[i]+1) continue;
else fr(j,L[i],R[i]) if(!nm[j]) {
return j;
}
}
}
void init(){
bk=sqrt(n);sz=pow(n,0.45);
fr(i,0,n) b[i]=i/bk+1;
gg=n/bk+1;
fr(i,1,gg) L[i]=(i-1)*bk,R[i]=min(n,i*bk);
}
void fz(int x){
if(vis[x]) Gai(x,a[x],-1);
else Gai(x,a[x],1);
vis[x]^=1;
}
void Go(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
while(dep[y]>dep[x]) fz(y),y=f[y];
while(x!=y) fz(x),fz(y),x=f[x],y=f[y];
}
int main(){
scanf("%d%d",&n,&m);
init();
fr(i,1,n) scanf("%d",&a[i]),pre[i]=a[i];
for(int i=1,x,y;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);
}
dfs(1,0),Gao();
int T=0,tot=0;
fr(i,1,m){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
if(x) q[++tot]=(Query){tot,y,z,T};
else c[++T]=(Change){y,pre[y],z},pre[y]=z;
}
T=0;
int nu=1,nv=1;
sort(q+1,q+1+tot,cmp);
fr(i,1,tot){
while(q[i].T>T) T++,Upd(c[T].x,c[T].od,c[T].nw);
while(q[i].T<T) Upd(c[T].x,c[T].nw,c[T].od),T--;
if(q[i].v!=nv) Go(q[i].v,nv),nv=q[i].v;
if(q[i].u!=nu) Go(q[i].u,nu),nu=q[i].u;
int lca=GetLCA(mk[q[i].u],mk[q[i].v]);
fz(lca);
d[q[i].id]=Get();
fz(lca);
}
fr(i,1,tot) printf("%d\n",d[i]);
return 0;
}