从莫队到树上莫队
-
莫队
-
离线处理查询问题的解决方法。
-
询问离线,第一关键字 l l l所在块,第二关键字 r r r的绝对位置。
-
设 块 大 小 为 n x , 左 端 点 移 动 n 1 + x 次 , 右 端 点 移 动 n 1 − x + 1 次 , x = 0.5 设块大小为n^x,左端点移动n^{1+x}次,右端点移动n^{1-x+1}次,x=0.5 设块大小为nx,左端点移动n1+x次,右端点移动n1−x+1次,x=0.5
-
核心代码
//创建块 m=sqrt(n);bel[0]=1; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); bel[i]=i/m+1; } //排序 bool operator<(nod a,nod b) { return bel[a.l]!=bel[b.l] ? bel[a.l]<bel[b.l] : a.r<b.r; } //莫队 int nl=0,nr=0;ll res=0; for(int i=1;i<=q;i++) { int l=query[i].l,r=query[i].r; while(nr<r) nr++,cntr[a[nr]]++,res+=cntl[a[nr]]; while(nl<l) nl++,cntl[a[nl]]++,res+=cntr[a[nl]]; while(nr>r) res-=cntl[a[nr]],cntr[a[nr]]--,nr--; while(nl>l) res-=cntr[a[nl]],cntl[a[nl]]--,nl--; ans[query[i].id]+=(((ll)query[i].flag)*res); }
-
-
带修莫队
-
带修莫队显然就是增加修改操作的莫队
-
询问离线,第一关键字l所在块,第二关键字r所在块,第三关键字时间。
-
设 块 大 小 为 n x , 左 端 点 移 动 n 1 + x , 右 端 点 移 动 n 2 − x + n 1 + x , 时 间 端 点 移 动 n 1 − x + 1 − x + 1 , x = 2 3 设块大小为n^x,左端点移动n^{1+x},右端点移动n^{2-x}+n^{1+x},时间端点移动n^{1-x+1-x+1},x=\frac 2 3 设块大小为nx,左端点移动n1+x,右端点移动n2−x+n1+x,时间端点移动n1−x+1−x+1,x=32
-
//创建块 m=pow(n,2.0/3.0); for(int i=1;i<=n;i++) { scanf("%d",&col[i]);temp[i]=col[i]; bel[i]=((i-1)/m)+1; } //排序 bool operator<(nod1 a,nod1 b) { if(bel[a.l]!=bel[b.l]) return bel[a.l]<bel[b.l]; if(bel[a.r]!=bel[b.r]) return bel[a.r]<bel[b.r]; return a.pos<b.pos; } //更新状态 //注意,更改时间操作的时候需要维护前一个时间是谁,方便回退状态 void update(int now) { if(cnt[col[now]]==0) res++; cnt[col[now]]++; } void erase(int now) { cnt[col[now]]--; if(cnt[col[now]]==0) res--; } void update(int now,int nl,int nr) { if(nl<=modify[now].u && modify[now].u<=nr) erase(modify[now].u); col[modify[now].u]=modify[now].now; if(nl<=modify[now].u && modify[now].u<=nr) update(modify[now].u); } void erase(int now,int nl,int nr) { if(nl<=modify[now].u && modify[now].u<=nr) erase(modify[now].u); col[modify[now].u]=modify[now].pre; if(nl<=modify[now].u && modify[now].u<=nr) update(modify[now].u); } //第一步可以强行更新,防止边界出错 int nl=query[1].l,nr=query[1].r,nt=query[1].pos; for(int i=nl;i<=nr;i++) update(i); for(int i=1;i<=nt;i++) update(i,nl,nr); ans[query[1].id]=res; for(int i=2;i<=tot1;i++) { int l=query[i].l,r=query[i].r,t=query[i].pos; while(nr<r) nr++,update(nr); while(l<nl) nl--,update(nl); while(r<nr) erase(nr),nr--; while(nl<l) erase(nl),nl++; while(nt<t) nt++,update(nt,l,r); while(t<nt) erase(nt,l,r),nt--; ans[query[i].id]=res; }
-
-
树上莫队
-
树上莫队NB!
-
考虑全 d f s dfs dfs序,点 p p p到点 q q q 之间只出现过一次的点,再加上点 p p p与点 q q q的 l c a lca lca,就是p到q路径上的点。
-
然后莫队就行了
-
这里 比较需要注意的是query的时候l和r到底选哪个,分情况讨论。
-
//CF GYM 100962F #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #define maxn 200005 using namespace std; typedef long long ll; struct nod { int l,r,id; nod(int a,int b,int c) { l=a; r=b; id=c; } nod(){} }query[maxn]; int n,q,tot,bel[maxn],m; int head[maxn],nex[maxn],to[maxn],w[maxn]; void add(int x,int y,int z) { to[++tot]=y; nex[tot]=head[x]; w[tot]=z; head[x]=tot; } bool operator<(nod a,nod b) { return bel[a.l]!=bel[b.l] ? bel[a.l]<bel[b.l] : a.r<b.r; } int dfn[maxn],val[maxn],in[maxn],out[maxn]; void dfs(int now,int fa) { in[now]=++tot; dfn[tot]=now; for(int i=head[now];i;i=nex[i]) { if(to[i]!=fa) { val[to[i]]=min(n,w[i]); dfs(to[i],now); } } out[now]=++tot; dfn[tot]=now; } int l[maxn],r[maxn]; int cnt[maxn],sum[maxn],vis[maxn]; void update(int now) { if(vis[now]==0) { vis[now]=1; cnt[val[now]]++; if(cnt[val[now]]==1) sum[bel[val[now]]]++; } else { vis[now]=0; cnt[val[now]]--; if(cnt[val[now]]==0) sum[bel[val[now]]]--; } } int ans[maxn]; int getans() { for(int i=1;i<=(n-1)/m+1;i++) { if(sum[i]!=(r[i]-l[i]+1)) { for(int j=l[i];j<=r[i];j++) { if(!cnt[j]) { return j-1; } } } } return n; } int main() { memset(l,0x3f,sizeof(l)); scanf("%d%d",&n,&q); m=320; for(int i=1;i<=n;i++) { bel[i]=(i-1)/m+1; l[bel[i]]=min(l[bel[i]],i); r[bel[i]]=max(r[bel[i]],i); } for(int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); z++; add(x,y,z); add(y,x,z); } tot=0; dfs(1,-1); for(int i=1;i<=q;i++) { int u,v; scanf("%d%d",&u,&v); query[i].id=i; if(in[u]>in[v]) swap(u,v); //这里是边权 query[i].l=in[u]+1; query[i].r=in[v]; //如果是点权 //if(lca==u){query[i].l = in[u];query[i].r = in[v];} //else {query[i].l = out[u];query[i].r = in[v];} } sort(query+1,query+1+q); int nl=1,nr=0; for(int i=1;i<=q;i++) { int l=query[i].l,r=query[i].r; while(nr<r) nr++,update(dfn[nr]); while(l<nl) nl--,update(dfn[nl]); while(r<nr) update(dfn[nr]),nr--; while(nl<l) update(dfn[nl]),nl++; //本题是边权不含lca //如果是点权要加一句 if(!cnt[val[query[i].lca]]) update() ans[query[i].id]=getans(); } for(int i=1;i<=q;i++) { printf("%d\n",ans[i]); } }
-
-
其他
- o ( 1 ) o(1) o(1)操作, o ( n ) o(\sqrt n) o(n)的分块和莫队更配哦。