18 2 C

112 篇文章 0 订阅

这里写图片描述
这里写图片描述
可以分别考虑每一个二进制位对答案的贡献。即,对于位 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值