树上莫队复习
树上莫队复习
被开业 D 了所以就来写 md
定义
树🌲上的莫队
应用
- 给你一棵 \(n\) 个节点的树,每个节点有一个颜色
- 让你求 \(u\) 到 \(v\) 的路径上不同颜色点的个数
前置知识
欧拉序
- 入的时候加一次,出的时候加一次
- 用于把树上问题抓换到序列上
树上莫队
- 考虑解决一开始的问题
- 设 \(st_i\) 表示 \(i\) 点入的时间,\(ed_i\) 表示 \(i\) 点出的时间
- 不妨设 \(st_x<st_y\) (先访问 \(x\) 后访问 \(y\))
- 若 \({\rm lca}(x,y)=x\) ,那么 \(x,y\) 处于同一条链上
- 于是我们仅需要统计 \(st_x\sim st_y\) 这一段上出现过一次的点
- 否则,此时 \(x,y\) 位于不同子树内,我们仅需统计 \(ed_x\sim st_y\) 以及 \(\rm lca\) 即可
- 因为 \(st_x\sim ed_x\) 是 \(x\) 为根的子树
- 然后,树上的问题成功被我们转化到序列上
- 剩下的交给莫队解决
代码
/*************************************************************************
> File Name: code.cpp
> Author: Typedef
> Mail: 1815979752@qq.com
> Created Time: 2021-10-07 08:17:32
> blog: https://www.cnblogs.com/Illyasviel
************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+7,RT=1e3+7;
int h[N],e[N<<1],ne[N<<1],idx=0;
int idfn[N<<1],st[N],ed[N],timestamp=0;
int fa[N],dep[N],son[N],top[N],sz[N];
int n,m;
inline void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs0(int u,int father){
st[u]=++timestamp;
idfn[timestamp]=u;
dep[u]=dep[father]+1;
son[u]=0,sz[u]=1,fa[u]=father;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v==father) continue;
dfs0(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
ed[u]=++timestamp;
idfn[timestamp]=u;
}
void dfs1(int u){
if(son[u]){
top[son[u]]=top[u];
dfs1(son[u]);
}
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v==fa[u]||v==son[u]) continue;
top[v]=v,dfs1(v);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
x^=y^=x^=y;
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int a[N],b[N],cnt[N<<1];
bool vis[N];
int ans[N];
int len=0,res=1;
int l=1,r;
inline int getid(int x){
return (x-1)/len;
}
inline void add(int x){
// puts("a");
if(!cnt[a[x]]) res++;
cnt[a[x]]++;
}
inline void del(int x){
// puts("d");
cnt[a[x]]--;
if(!cnt[a[x]]) res--;
}
inline void mov(int x){
if(vis[x]) del(x);
else add(x);
vis[x]^=1;
}
struct Query{
int l,r;
int anc,id;
friend bool operator<(const Query &a,const Query &b){
return getid(a.l)==getid(b.l)?((getid(a.l)&1)?(a.r<b.r):(a.r>b.r)):getid(a.l)<=getid(b.l);
}
}q[N];
template<class T>void qread(T &x){
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
if(f) x=-x;
}
template<class T>void print(T x,char c='\n'){
vector<char> stk;
if(x<0) putchar('-'),x=-x;
while(x) stk.push_back(x%10+'0'),x/=10;
while(!stk.empty()) putchar(stk.back()),stk.pop_back();
putchar(c);
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
memset(h,-1,sizeof(h));
qread(n),qread(m);
len=(int)sqrt(n);
for(int i=1;i<=n;i++)
qread(a[i]);
memcpy(b,a,sizeof(a));
sort(b+1,b+n+1);
int tot=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+tot+1,a[i])-a;
ans[1]=0,top[1]=1;
for(int i=1;i<n;i++){
int x,y;
qread(x),qread(y);
add(x,y),add(y,x);
}
dfs0(1,0),dfs1(1);
for(int i=1;i<=m;i++){
int x,y;
qread(x),qread(y);
q[i].anc=lca(x,y);
q[i].id=i;
if(st[x]>st[y])
x^=y^=x^=y;
q[i].r=st[y];
if(q[i].anc==x)
q[i].l=st[x],q[i].anc=0;
else q[i].l=ed[x];
}
sort(q+1,q+m+1);
for(int i=1;i<=m;i++){
int ql=q[i].l,qr=q[i].r;
while(r<qr) mov(idfn[++r]);
while(r>qr) mov(idfn[r--]);
while(l<ql) mov(idfn[l++]);
while(l>ql) mov(idfn[--l]);
if(q[i].anc) mov(q[i].anc);
ans[q[i].id]=res;
if(q[i].anc) mov(q[i].anc);
}
for(int i=1;i<=m;i++)
print(ans[i]-1);
return 0;
}
/*/
_________ __ _-----_
__/ ________\_/ \/------->
/ __/ \ (@)_\___
/ / _----- _/ / \_\_
/ / / \_ \\
/ / / \_ \
/ / / _ \
/ / / / \___ |
/ / / / \___ /
| | / / \___ /
/ / / / / /
| | / / / /
/ / / / / /
| | / / / /
/__\/ / / /
|\ _\/__ / /
/ // /\__\___ / /
/_// / \___\___ / /
_/_// \__ \___\ / /
/ // \___ \/ /
/ // \_____/ /
/ // /
\_/ \_ /
\__ /
\___ /
\_____ /
*/