[BZOJ3757]苹果树(树上莫队)

时间:2021-10-29 14:37:39

树上莫队共有三种写法:

  1.按DFS序列分块,和普通莫队类似。常数大,不会被卡。

  2.按块状树的方式分块。常数小,会被菊花图卡到O(n)。

  3.按[BZOJ1086]王室联邦的方式分块。常数小,不会被卡。唯一的缺点是较抽象,一个块可能是不连通的。

权衡一下当然还是写第三种做法,具体看代码。

然后还有一个问题,手动模拟莫队移动左右端点指针的过程,会发现LCA处较难处理,它常常是跟其它点反着的。于是我们每次移指针的时候都忽略LCA,最后询问的时候加上LCA求解答案再减去LCA。再模拟会发现,所有方案都可以处理了。

以及要注意每个询问如果左端点所在块编号比右端点所在块大则需要交换左右端点。询问的排序方式是:若两端点不在同一块则按块编号排序,否则按DFS序排序。也就是按(bel[i],dfn[i])的双关键字排序。

 #include<cmath>
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std; const int N=;
int n,m,B,u,v,tim,top,tot,a[N],stk[N],b[N],dep[N],fa[N][];
int cnt,res,rt,vis[N],ans[N],dfn[N],s[N],h[N],to[N],nxt[N];
struct P{ int l,r,x,y,id; }q[N]; bool cmp(const P &x,const P &y){ return b[x.l]==b[y.l] ? dfn[x.r]<dfn[y.r] : b[x.l]<b[y.l]; }
void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x){
dfn[x]=++tim; int tmp=top;
rep(i,,) fa[x][i]=fa[fa[x][i-]][i-];
For(i,x) if ((k=to[i])!=fa[x][]){
fa[k][]=x; dep[k]=dep[x]+; dfs(k);
if (top-tmp>=B){ ++tot; while (top!=tmp) b[stk[top--]]=tot; }
}
stk[++top]=x;
} int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int t=dep[x]-dep[y];
for (int i=; ~i; i--) if (t&(<<i)) x=fa[x][i];
if (x==y) return x;
for (int i=; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][];
} void upd(int x){
if (vis[x]){ s[a[x]]--; if (!s[a[x]]) res--; }
else { s[a[x]]++; if (s[a[x]]==) res++; }
vis[x]^=;
} void work(int x,int y){
for (; x!=y; upd(x),x=fa[x][])
if (dep[x]<dep[y]) swap(x,y);
} int main(){
freopen("bzoj3757.in","r",stdin);
freopen("bzoj3757.out","w",stdout);
scanf("%d%d",&n,&m); B=sqrt(n);
rep(i,,n) scanf("%d",&a[i]);
rep(i,,n){
scanf("%d%d",&u,&v);
if (!u || !v) { rt=u+v; continue; }
add(u,v); add(v,u);
}
dfs(rt);
while (top) b[stk[top--]]=tot;
rep(i,,m){
scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].x,&q[i].y); q[i].id=i;
if (b[q[i].l]>b[q[i].r]) swap(q[i].l,q[i].r);
}
sort(q+,q+m+,cmp);
int L=rt,R=rt;
rep(i,,m){
work(L,q[i].l); work(R,q[i].r); L=q[i].l; R=q[i].r;
int f=lca(L,R); upd(f);
ans[q[i].id]=res-(int)(q[i].x!=q[i].y&&s[q[i].x]&&s[q[i].y]); upd(f);
}
rep(i,,m) printf("%d\n",ans[i]);
return ;
}