思路:树剖后序列分块,查询时每段答案分块 bitset 合并。
Rt,悬关。
个人感觉复杂度没问题,本地造大数据能在 1s 内跑出来,但是提交第一个点就 TLE 了。
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx")
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const ll N=40040,M=2*sqrt(N);
inline ll read(){
register ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,m,k,x,y,h,cnt=0,sum=0;
ll a[N],b[N];
ll fa[N],p[N],z[N],d[N],t[N];
ll A[N],B[N],W[N];
ll T[N],L[M],R[M];
bitset<N> ans,f;
bitset<N> S[M];
vector<ll> E[N];
inline void add(ll u,ll v){
E[u].push_back(v);
E[v].push_back(u);
}
inline void dfs1(register ll u,register ll f){
p[u]=1;
for(register auto v:E[u]){
if(v==f)
continue;
fa[v]=u;
d[v]=d[u]+1;
dfs1(v,u);
p[u]+=p[v];
if(p[v]>p[z[u]])
z[u]=v;
}
}
inline void dfs2(register ll u,register ll k){
A[u]=++sum;
B[sum]=u;
W[sum]=a[u];
t[u]=k;
if(!z[u])
return ;
dfs2(z[u],k);
for(register auto v:E[u]){
if(v==fa[u]||v==z[u])
continue;
dfs2(v,v);
}
}
inline bitset<N> qurey(register ll x,register ll y){
f.reset();
register ll l=T[x],r=T[y];
if(l==r){
for(register int i=x;i<=y;i++)
f[W[i]]=1;
}
else{
for(register int i=x;i<=R[l];i++)
f[W[i]]=1;
for(register int i=L[r];i<=y;i++)
f[W[i]]=1;
for(register int i=(l+1);i<=(r-1);i++)
f|=S[i];
}
return f;
}
inline ll Find(ll u,ll v){
ans.reset();
while(t[u]!=t[v]){
if(d[t[u]]<d[t[v]])
swap(u,v);
ans|=qurey(A[t[u]],A[u]);
u=fa[t[u]];
}
if(d[u]>d[v])
swap(u,v);
ans|=qurey(A[u],A[v]);
return ans.count();
}
int main(){
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
n=read(),m=read();
k=sqrt(n),h=n/k;
if(n%k!=0)
h++;
for(register int i=1;i<=h;i++){
L[i]=(i-1)*k+1;
R[i]=i*k;
}
R[h]=n;
for(register int i=1;i<=n;i++){
a[i]=read();
b[++cnt]=a[i];
T[i]=(i-1)/k+1;
}
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-(b+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
for(register int u,v,i=1;i<n;i++){
u=read(),v=read();
add(u,v);
}
dfs1(1,1);
dfs2(1,1);
for(register int i=1;i<=n;i++)
S[T[i]][W[i]]=1;
while(m--){
x=read(),y=read();
write(Find(x,y));
putchar('\n');
}
return 0;
}