莫队
如果知道[l,r]的答案时能快速求出[l+1,r][l,r+1][l-1,r][l,r-1]的答案,那么可以用莫队离线求解
如果要从[l,r]得到[l',r'],那么需要$O(|r'-r|+|l'-l|)$次更新答案
所以需要确定一个求答案的顺序使得这玩意最优
以$\frac{n}{q}$分块,按l所在块为第一关键字,r为第二关键字排序
总复杂度是$O(n \sqrt q)$
luogu1494 小Z的袜子
询问$\sum{cnt[i]*(cnt[i]-1)}$,其中cnt[i]是每种颜色出现的次数
可以发现当$cnt[i]+=d$时,这玩意会$+=d^2+2*cnt[i]*d-d$
于是记每种颜色出现的次数就好了
1 #include<bits/stdc++.h> 2 #define CLR(a,x) memset(a,x,sizeof(a)) 3 using namespace std; 4 typedef long long ll; 5 typedef unsigned long long ull; 6 typedef pair<int,int> pa; 7 const int maxn=5e4+10; 8 9 inline ll rd(){ 10 ll x=0;char c=getchar();int neg=1; 11 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 12 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 13 return x*neg; 14 } 15 16 int N,M,col[maxn],cnt[maxn],sqn; 17 ll ans[maxn],len[maxn]; 18 struct Node{ 19 int l,r,i; 20 }q[maxn]; 21 22 inline bool cmp(Node a,Node b){ 23 return a.l/sqn==b.l/sqn?a.r<b.r:a.l<b.l; 24 } 25 26 inline ll gcd(ll x,ll y){ 27 if(!x) return y; 28 return gcd(y%x,x); 29 } 30 31 int main(){ 32 //freopen("","r",stdin); 33 int i,j,k; 34 N=rd(),M=rd();sqn=sqrt(N); 35 for(i=1;i<=N;i++) col[i]=rd(); 36 for(i=1;i<=M;i++){ 37 q[i].l=rd(),q[i].r=rd(),q[i].i=i; 38 len[i]=1ll*(q[i].r-q[i].l+1)*(q[i].r-q[i].l); 39 } 40 sort(q+1,q+M+1,cmp); 41 int l=1,r=0; 42 ll tmp=0; 43 for(i=1;i<=M;i++){ 44 if(q[i].l==q[i].r) len[q[i].i]=1; 45 while(r<q[i].r){ 46 tmp+=cnt[col[++r]]*2; 47 cnt[col[r]]++; 48 } 49 while(r>q[i].r){ 50 tmp+=2-cnt[col[r]]*2; 51 cnt[col[r--]]--; 52 } 53 while(l>q[i].l){ 54 tmp+=cnt[col[--l]]*2; 55 cnt[col[l]]++; 56 } 57 while(l<q[i].l){ 58 tmp+=2-cnt[col[l]]*2; 59 cnt[col[l++]]--; 60 } 61 ans[q[i].i]=tmp/gcd(tmp,len[q[i].i]),len[q[i].i]/=gcd(tmp,len[q[i].i]); 62 } 63 for(i=1;i<=M;i++){ 64 65 printf("%lld/%lld\n",ans[i],len[i]); 66 } 67 return 0; 68 }
luogu3709 大爷的字符串题
描述比较迷,但是分析以后给一个构造就是每次那个区间最少能给他排成几个严格递增的序列,不难发现就是众数的个数
于是莫队,记$cnt[i]$是$i$出现的次数,$num[j]$是$cnt$为$j$出现的次数,以及目前的最大$cnt$,我这个最大的$cnt$的数量被减没了,就换到原来的最大的$-1$
1 #include<bits/stdc++.h> 2 #define CLR(a,x) memset(a,x,sizeof(a)) 3 #define MP make_pair 4 using namespace std; 5 typedef long long ll; 6 typedef unsigned long long ull; 7 typedef pair<int,int> pa; 8 const int maxn=2e5+10; 9 10 inline ll rd(){ 11 ll x=0;char c=getchar();int neg=1; 12 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 13 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 14 return x*neg; 15 } 16 17 int N,NN,M,a[maxn],tmp[maxn]; 18 int ans[maxn],cnt[maxn],num[maxn]; 19 struct Node{ 20 int l,r,i; 21 }q[maxn]; 22 23 inline bool cmp(Node x,Node y){ 24 return x.l/NN==y.l/NN?x.r<y.r:x.l<y.l; 25 } 26 27 int main(){ 28 //freopen("","r",stdin); 29 int i,j,k; 30 N=rd(),M=rd();NN=sqrt(N); 31 for(i=1;i<=N;i++){ 32 a[i]=tmp[i]=rd(); 33 }sort(tmp+1,tmp+N+1); 34 j=unique(tmp+1,tmp+N+1)-tmp; 35 for(i=1;i<=N;i++) 36 a[i]=lower_bound(tmp+1,tmp+j,a[i])-tmp; 37 for(i=1;i<=M;i++){ 38 q[i].l=rd(),q[i].r=rd(),q[i].i=i; 39 ans[i]=0; 40 } 41 sort(q+1,q+M+1,cmp); 42 43 int ma=0,l=1,r=0; 44 for(i=1;i<=M;i++){ 45 while(r<q[i].r){ 46 cnt[a[++r]]++; 47 num[cnt[a[r]]]++,num[cnt[a[r]]-1]--; 48 if(cnt[a[r]]>ma) ma=cnt[a[r]]; 49 }while(r>q[i].r){ 50 cnt[a[r]]--; 51 num[cnt[a[r]]]++,num[cnt[a[r]]+1]--; 52 if(!num[ma]) ma--; 53 r--; 54 } 55 while(l>q[i].l){ 56 cnt[a[--l]]++; 57 num[cnt[a[l]]]++,num[cnt[a[l]]-1]--; 58 if(cnt[a[l]]>ma) ma=cnt[a[l]]; 59 }while(l<q[i].l){ 60 cnt[a[l]]--; 61 num[cnt[a[l]]]++,num[cnt[a[l]]+1]--; 62 if(!num[ma]) ma--; 63 l++; 64 } 65 ans[q[i].i]-=ma; 66 } 67 for(i=1;i<=M;i++) 68 printf("%d\n",ans[i]); 69 return 0; 70 }
树上莫队
针对树上路径的询问
欧拉序做法
考虑一棵树的欧拉序(即括号序,进和出时各记一次),如果对于一个区间,某个元素出现了两次,则认为它没出现过,那么对于路径(a,b)有两种情况(不妨设a在b前面):
1.$a$或$b$中有一个是$lca$:$[in[a],in[b]]$
2.否则:$[out[a],in[b]]U\{lca\}$
画图理解即可
于是就变成了和普通莫队一样。但需要注意的是这个区间不能拆,所以有一些题做不了...
树分块做法
考虑按某种方法对树分块,最后排序时的关键字改为那个点所属的块的编号
考虑dfs时维护一个栈,进入点x时记下栈中元素个数,每从一个x的子树出来时,判断一下新增的元素个数是否大于$B$(块大小),如果大于的话,把新增的这些点分到同一个块里;出x的时候将x计入栈中。最后剩下的元素扔到最后一个块里
这样可以保证块大小都是$[B,3B]$的
1 void dfs(int x){ 2 int t=sh; 3 for(int i=egh[x];i;i=eg[i][1]){ 4 int b=eg[i][0];if(b==fa[x][0]) continue; 5 fa[b][0]=x,dep[b]=dep[x]+1;dfs(b); 6 if(sh-t>=blk){ 7 ++bct; 8 for(;sh>t;sh--) bel[stk[sh]]=bct; 9 } 10 } 11 stk[++sh]=x; 12 }
然后处理询问的时候,要从(a,b)到(c,b),只要考虑(a,c)路径上的点即可
带修莫队
可以将修改视为第三维来做,复杂度$O\left(\frac{qn^2}{B^2}+qB+\frac{n^2}{B}\right)$,B是块大小
$n=q$时可以取$B=n^{\frac{2}{3}}$,复杂度是$O(n^{\frac{5}{3}})$
[WC2013]糖果公园
带修树上莫队
1 #include<bits/stdc++.h> 2 #include<tr1/unordered_map> 3 #define CLR(a,x) memset(a,x,sizeof(a)) 4 #define MP make_pair 5 #define fi first 6 #define se second 7 #define oct hakheaw 8 using namespace std; 9 typedef long long ll; 10 typedef unsigned long long ull; 11 typedef long double ld; 12 typedef pair<int,int> pa; 13 const int maxn=1e5+10,Blk=2154; 14 15 inline ll rd(){ 16 ll x=0;char c=getchar();int neg=1; 17 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 18 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 19 return x*neg; 20 } 21 22 struct Node{ 23 int l,r,x,t,i; 24 Node(int _l=0,int _r=0,int _x=0,int _t=0,int _i=0){l=_l,r=_r,x=_x,t=_t,i=_i;} 25 }qr[maxn]; 26 int N,M,Q,val[maxn],eg[maxn*2][2],egh[maxn],ect,col[maxn],lik[maxn]; 27 int dfn[maxn][2],tot,id[maxn*2],fa[maxn][20],dep[maxn]; 28 int cg[maxn][3],oct,qct; 29 int cnt[maxn]; 30 ll ans[maxn]; 31 bool flag[maxn]; 32 inline void adeg(int a,int b){ 33 eg[++ect][0]=b,eg[ect][1]=egh[a],egh[a]=ect; 34 } 35 36 void dfs(int x){ 37 dfn[x][0]=++tot;id[tot]=x; 38 for(int i=0;fa[x][i]&&fa[fa[x][i]][i];i++) fa[x][i+1]=fa[fa[x][i]][i]; 39 for(int i=egh[x];i;i=eg[i][1]){ 40 int b=eg[i][0];if(b==fa[x][0]) continue; 41 fa[b][0]=x; 42 dep[b]=dep[x]+1;dfs(b); 43 } 44 dfn[x][1]=++tot;id[tot]=x; 45 } 46 47 inline int getlca(int x,int y){ 48 if(dep[x]<dep[y]) swap(x,y); 49 for(int i=19;i>=0;i--){ 50 if(fa[x][i]&&dep[fa[x][i]]>=dep[y]) x=fa[x][i]; 51 } 52 if(x==y) return x; 53 for(int i=19;i>=0;i--){ 54 if(fa[x][i]&&fa[y][i]&&fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; 55 } 56 return fa[x][0]; 57 } 58 59 inline bool cmp(Node a,Node b){ 60 return (a.l/Blk==b.l/Blk)?((a.r/Blk==b.r/Blk?(a.i<b.i):a.r<b.r)):a.l<b.l; 61 } 62 63 inline ll inc(int c){ 64 return 1ll*val[c]*lik[++cnt[c]]; 65 } 66 67 inline ll dec(int c){ 68 return -1ll*val[c]*lik[cnt[c]--]; 69 } 70 71 inline ll deal(int x){ 72 // printf("DEAL:%d\n",x); 73 flag[x]^=1; 74 if(flag[x]) return inc(col[x]); 75 else return dec(col[x]); 76 } 77 78 inline ll change(int x,int y){ 79 ll re=0; 80 if(flag[x]) re+=dec(col[x])+inc(y); 81 // printf("CG:%d %d %d %d\n",x,y,col[x],re); 82 col[x]=y; 83 return re; 84 } 85 86 int main(){ 87 //freopen("","r",stdin); 88 N=rd(),M=rd(),Q=rd(); 89 for(int i=1;i<=M;i++) val[i]=rd(); 90 for(int i=1;i<=N;i++) lik[i]=rd(); 91 for(int i=1;i<N;i++){ 92 int a=rd(),b=rd(); 93 adeg(a,b),adeg(b,a); 94 } 95 for(int i=1;i<=N;i++) col[i]=rd(); 96 dep[1]=1;dfs(1); 97 // for(int i=1;i<=tot;i++) printf("~%d %d\n",i,id[i]); 98 for(int i=1;i<=Q;i++){ 99 int a=rd(),b=rd(),c=rd(); 100 if(!a) cg[++oct][0]=b,cg[oct][1]=c,cg[oct][2]=col[b],col[b]=c; 101 else{ 102 if(dfn[b][0]>dfn[c][0]) swap(b,c); 103 int lca=getlca(b,c); 104 if(lca==b) qr[++qct]=Node(dfn[b][0],dfn[c][0],lca,oct,i); 105 else qr[++qct]=Node(dfn[b][1],dfn[c][0],lca,oct,i); 106 } 107 } 108 sort(qr+1,qr+qct,cmp); 109 ll now=0;int l=1,r=0,t=oct; 110 for(int i=1;i<=qct;i++){//if(i==2) continue; 111 int x=qr[i].l,y=qr[i].r,z=qr[i].t; 112 while(t<z) ++t,now+=change(cg[t][0],cg[t][1]); 113 while(t>z) now+=change(cg[t][0],cg[t][2]),t--; 114 while(r<y) now+=deal(id[++r]); 115 while(r>y) now+=deal(id[r--]); 116 while(l<x) now+=deal(id[l++]); 117 while(l>x) now+=deal(id[--l]); 118 ans[qr[i].i]=now; 119 if(qr[i].x!=id[qr[i].l]) ans[qr[i].i]+=deal(qr[i].x),deal(qr[i].x); 120 } 121 for(int i=1;i<=Q;i++) if(ans[i]) printf("%lld\n",ans[i]); 122 return 0; 123 }