可以分别考虑每一个二进制位对答案的贡献。即,对于位
2
x
2^x
2x,维护从每一个点
t
t
t 出发,向上
2
i
2^i
2i 的距离之内,与
t
t
t 距离为
d
d
d 满足
d
a
n
d
2
x
=
2
x
d \mathbin{\mathrm{and}} 2^x = 2^x
dand2x=2x 且点权的二进制表示中包含
2
x
2^x
2x 的点的个数就行了。 由于路径有向上的部分,也有向下的部分,因此还需要维护满足
d
a
n
d
2
x
=
0
d \mathbin{\mathrm{and}} 2^x = 0
dand2x=0 的点的个数在从
v
v
v 倍增的时候使用。
其实并不需要对于每一个位分别维护点的个数和,只需要维护所有重叠的位的数位和就行了,于是似乎可以少掉一个 log \log log。
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 300030
#define logn 20
#define ll long long
using namespace std;
int n,q,a[maxn],tot,la[maxn],fa[maxn][logn],deep[maxn],cnt[maxn][logn];
ll s1[maxn][logn],s0[maxn][logn];
struct edge{int v,ne;}e[maxn<<1];
inline void add(int u,int v){
e[tot].v=v,e[tot].ne=la[u],la[u]=tot++;
}
inline void dfs(int u){
s1[u][0]=s0[u][0]=a[u];
for(int i=0;i<logn;++i){
cnt[u][i]=cnt[fa[u][0]][i]+(!((a[u]>>i)&1));
if(!i)continue;
fa[u][i]=fa[fa[u][i-1]][i-1];
s1[u][i]=s1[u][i-1]+s1[fa[u][i-1]][i-1]+(1ll<<(i-1))*(cnt[fa[u][i-1]][i-1]-cnt[fa[u][i]][i-1]);
s0[u][i]=s0[u][i-1]+s0[fa[u][i-1]][i-1]+(1ll<<(i-1))*(cnt[u][i-1]-cnt[fa[u][i-1]][i-1]);
}
for(int i=la[u];~i;i=e[i].ne){
int v=e[i].v;
if(v==fa[u][0])continue;
deep[v]=deep[u]+1;
fa[v][0]=u;dfs(v);
}
}
void init(){
tot=0;memset(la,-1,sizeof(la));
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
deep[1]=1;dfs(1);
}
inline int LCA(int u,int v)
{
if(deep[u]<deep[v])
swap(u,v);
for(int i=logn-1;~i;--i)
if(deep[fa[u][i]]>=deep[v])
u=fa[u][i];
if(u==v)
return u;
for(int i=logn-1;~i;--i)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
inline ll query_up(int u,int lca)
{
ll ans=a[lca];
for(int i=logn-1;~i;--i)
if(deep[fa[u][i]]>=deep[lca])
ans+=s1[u][i],u=fa[u][i],ans+=(1ll<<i)*(cnt[u][i]-cnt[fa[lca][0]][i]);
return ans;
}
inline ll query_up0(int u,int k)
{
ll ans=0;
int tmp=u;
for(int i=0;k;k>>=1,++i)
if(k&1)
ans+=s0[u][i]+(1ll<<i)*(cnt[tmp][i]-cnt[u][i]),u=fa[u][i];
return ans;
}
inline ll query_down(int u,int v,int lca)
{
int len=deep[u]+deep[v]-(deep[lca]<<1);
return query_up0(v,len+1)-query_up0(lca,len-(deep[v]-deep[lca])+1);
}
void solve()
{
while(q--)
{
int u,v,lca;
scanf("%d%d",&u,&v);
lca=LCA(u,v);
printf("%lld\n",query_up(u,lca)+query_down(u,v,lca));
}
}
int main(){
init();
solve();
return 0;
}
来源:zr