题意

给出一棵N 个节点的树,树上的每个节点都有一个权值$a_i$。

有Q 次询问,每次在树上选中两个点u, v,考虑所有在简单路径u, v 上(包括u, v)的点构成的集合S。

求$\sum_{w∈S}{a_w or dist(u,w)}$

其中dist(u,w) 为简单路径u,w 上的边数,or 是按位或。


思考

设f[i][j]表示从第i个节点开始,总共跳了$2^j$个节点得到的答案,g[i][j]表示从第i个节点开始,向下跳了$2^j$个节点得到的答案。这些状态的转移只要考虑后半部分最高位上1的贡献。

接下来对于查询u,v,我们算出其lca。对于u-lca,可以用倍增求出答案。由于倍增时每次的长度都至少会是以前的一半,那么某些二进制位上在以后一定都会是1,而且之前求出的f和它是独立的。对于lca-v的答案,可以先向上跳一些深度,再用g类似地求出答案。


代码

 // luogu-judger-enable-o2
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn=6E5+;
const int layer=;
int n,T;
int size,head[maxn*];
int root,dep[maxn];
ll val[maxn],fa[maxn][layer],cnt[maxn][layer],up[maxn][layer],down[maxn][layer];
inline int read()
{
char ch=getchar();
while(!isdigit(ch))
ch=getchar();
int sum=ch-'';
ch=getchar();
while(isdigit(ch))
{
sum=sum*+ch-'';
ch=getchar();
}
return sum;
}
void write(ll x)
{
if(x>=)
write(x/);
putchar(''+x%);
}
inline void writen(ll x)
{
write(x);
putchar('\n');
}
struct edge
{
int to,next;
}E[maxn*];
inline void add(int u,int v)
{
E[++size].to=v;
E[size].next=head[u];
head[u]=size;
}
void dfs(int u,int F,int d)
{
fa[u][]=F;
dep[u]=d;
for(int i=;i<layer;++i)
fa[u][i]=fa[fa[u][i-]][i-];
for(int i=;i<layer;++i)
cnt[u][i]=cnt[F][i]+((val[u]&(<<i))>);
up[u][]=down[u][]=val[u];
for(int i=;i<layer;++i)
{
up[u][i]=up[u][i-]+up[fa[u][i-]][i-]+(ll)(<<(i-))*((<<(i-))-cnt[fa[u][i-]][i-]+cnt[fa[u][i]][i-]);
down[u][i]=down[u][i-]+down[fa[u][i-]][i-]+(ll)(<<(i-))*((<<(i-))-cnt[u][i-]+cnt[fa[u][i-]][i-]);
}
for(int i=head[u];i;i=E[i].next)
{
int v=E[i].to;
if(v==F)
continue;
dfs(v,u,d+);
}
}
inline int jump(int x,int d)
{
for(int i=;i<layer;++i)
if(d&(<<i))
x=fa[x][i];
return x;
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
int d=dep[x]-dep[y];
x=jump(x,d);
if(x==y)
return x;
for(int i=layer-;i>=;--i)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][];
}
inline ll get1(int x,int to)
{
if(x==to)
return ;
ll sum=;
for(int i=layer-;i>=;--i)
if(dep[fa[x][i]]>=dep[to])
{
sum+=up[x][i];
x=fa[x][i];
sum+=(dep[x]-dep[to]-(cnt[x][i]-cnt[to][i]))*(ll)(<<i);
}
return sum;
}
int wait[],c[],tot;
inline ll get2(int x,int to)
{
if(x==to)
return val[x];
if(dep[x]>dep[to])
return ;
ll sum=;
tot=;
int from=to;
int d=dep[to]-dep[x]+;
for(int i=;i<layer;++i)
if(d&(<<i))
{
wait[++tot]=to;
c[tot]=i;
to=fa[to][i];
}
for(int i=tot;i>=;--i)
{
int u=wait[i];
sum+=down[u][c[i]];
if(c[i])
sum+=(ll)(<<(c[i]))*(dep[from]-dep[u]-(cnt[from][c[i]]-cnt[u][c[i]]));
}
return sum;
}
int main()
{
// freopen("C1.in","r",stdin);
// freopen("C.out","w",stdout);
ios::sync_with_stdio(false);
n=read(),T=read();
for(int i=;i<=n;++i)
val[i]=read();
for(int i=;i<=n;++i)
{
int x=read(),y=read();
add(x,y);
add(y,x);
}
root=n*;
add(n+,);
for(int i=n+;i<=root;++i)
add(i,i-);
dfs(root,root,);
while(T--)
{
int x,y,z,d;
x=read(),y=read();
z=lca(x,y);
d=dep[x]-dep[z];
int q=jump(z,d);
// cout<<x<<" "<<z<<" "<<q<<endl;
writen(get1(x,z)+get2(q,y)-get2(q,fa[z][]));
}
return ;
}

[校内训练19_09_02]C的更多相关文章

  1. [校内训练19_09_02]A

    题意 给出N 个形如$f_i(x) = a_i x^2 + b_i x $的二次函数. 有Q 次询问,每次给出一个x,询问$max{\{f_i(x)\}}$.$N,Q \leq 5*10^5$. 思考 ...

  2. [4.14校内训练赛by hzwer]

    来自FallDream的博客,未经允许,请勿转载,谢谢. hzwer又出丧题虐人 4道noi....        很奇怪 每次黄学长出题总有一题我做过了. 嗯题目你们自己看看呗 好难解释 ----- ...

  3. [2017.4.7校内训练赛by hzwer]

    来自FallDream的博客,未经允许,请勿转载,谢谢. 报警啦.......hzwer又出丧题虐人啦..... 4道ctsc...有一道前几天做过了,一道傻逼哈希还wa了十几次,勉强过了3题..我好 ...

  4. [3.24校内训练赛by hzwer]

    来自FallDream的博客,未经允许,请勿转载,谢谢. ----------------------------------------------------------------------- ...

  5. 19_04_19校内训练[Game]

    题意 给出n,等概率地生成一个1~n的数列.现在有n个人从左到右站成一排,每个人拿有当前数列位置上的数字,并且一开始都不知道数字是多少(但知道n是多少).从左到右让每个人进行如下选择: 1.选择保留自 ...

  6. 19_04_02校内训练[deadline]

    题意 给出一个二分图,左边为A集合,右边为B集合,要求把A集合中每一个点染为黑白两色中的一种,B集合中的颜色已定.染色后对于原本相邻且颜色相同的点,建立新的二分图,即得到了两个新的二分图,它们是独立的 ...

  7. 平面图转对偶图&19_03_21校内训练 [Everfeel]

    对于每个平面图,都有唯一一个对偶图与之对应.若G‘是平面图G的对偶图,则满足: G'中每一条边的两个节点对应着G中有公共边的面,包括最外部无限大的面. 直观地讲,红色标出来的图就是蓝色标出的图的对偶图 ...

  8. fzyzojP3979 -- [校内训练20180914]魔法方阵

    原题见CF632F https://blog.csdn.net/Steaunk/article/details/80217764 这个比较神仙了 点边转化, 把max硬生生转化成了路径最大值,再考虑所 ...

  9. fzyzojP3580 -- [校内训练-互测20180315]小基的高智商测试

    题目还有一个条件是,x>y的y只会出现一次(每个数直接大于它的只有一个) n<=5000 是[HNOI2015]实验比较 的加强版 g(i,j,k)其实可以递推:g(i,j,k)=g(i- ...

随机推荐

  1. Arcgis api for javascript学习笔记(3.2版本) - 匀速行驶轨迹动画效果

    一.前言 有这样一个需求:已知某条线上的n个点的经纬度数组 ,实现物体运行轨迹. 如果这些点中两个距离很近,那么我们可以用一个定时器在地图上每次重新画一个点,这样肉眼看到这个点上的运动效果,如下图代码 ...

  2. [Linux] 利用tcpdump和strace进行debug

    比如说要查看所有的sql查询语句,数据库的端口是3306 tcpdump -i any port 3306 -l -s 0 -w -|strings|grep -A 5 select 要查看所有的调用 ...

  3. flask迁移数据库时报错:Target database is not up的解决方案

    在flask中进行数据库迁移时报错,报错信息为"Target database is not up",解决方案如下 找到alembic的最新版本号,找到文件夹migrate下的最新 ...

  4. html页脚固定在底部的方法

    <style type="text/css"> html { height: 100%; } body { height: 100%; margin: 0; paddi ...

  5. 程序员初学者参考 ---懂得基础语法后如何做一个自己的case?

    对于很多人来说,我懂java语法,甚至面向对象的特性啦这些都是有了解的,但我就是不会做项目,其实项目真有那么难吗? 对于基础不牢固的人来说,我还不会这个基础点,那个还没学呢,你让我做个项目,我保证做不 ...

  6. 9.python中sys.argv[]用法说明

    在python中sys.argv[]是用来获取命令行输入的参数的(参数和参数之间空格区分),sys.argv[0]表示代码本身文件路径,所以从参数1开始,表示获取的参数了 举例说明:创建一个程序名为t ...

  7. Python学习中的“按位取反”笔记总结

    | 疑惑 最近在学习Python的过程中了解到位运算符,但对于按位取反有点迷糊,就比如说~9(按位取反)之后的结果是-10,为什么不是6呢?所以下面就来看看为什么不是6,正确结果是如何计算出来的呢? ...

  8. python中end=''

    end = ''  用于连接下一条的print输出内容 效果图: 代码: # end='' 用于连接下一条输出语句 print('哈哈哈') print('嘻嘻嘻') print('\n\n') pr ...

  9. Spring-cloud微服务实战【一】:微服务的概念与演进过程

    本文是一个系列文章,主要讲述使用spring-cloud进行微服务开发的实战.在开始之前,我们先说一下从传统的单一部署架构到微服务的发展过程,以便让童鞋们更好的理解微服务的概念与演进过程. 1.单体架 ...

  10. windows服务搭建(VS2019创建Windows服务不显示安装组件)

    1.创建windows服务应用 2.右键查看代码 3.写个计时器Timer  using System.Timers; 如上图,按tab键快速操作  会自动创建一个委托 改为下边的方式,打印日志来记录 ...