题意

给出一棵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. POJ3237 Tree 树链剖分 边权

    POJ3237 Tree 树链剖分 边权 传送门:http://poj.org/problem?id=3237 题意: n个点的,n-1条边 修改单边边权 将a->b的边权取反 查询a-> ...

  2. 【转】Elasticsearch学习笔记

    一.常用术语 索引(Index).类型(Type).文档(Document) 索引Index是含有相同属性的文档集合.索引在ES中是通过一个名字来识别的,且必须是英文字母小写,且不含中划线(-):可类 ...

  3. CString::CompareNoCase与CString::Compare的区别

    转载:https://blog.csdn.net/lingdxuyan/article/details/4362116 函数原型:int CompareNoCase( LPCTSTR lpsz ) c ...

  4. 写时拷贝COW(copy-on-write)

        写时拷贝技术是通过"引用计数"实现的,在分配空间的时候多分配4个字节,用来记录有多少个指针指向块空间,当有新的指针指向这块空间时,引用计数加一,当要释放这块空间时,引用计数 ...

  5. spring-redis-session 自定义 key 和过期时间

    对于分布式应用来说,最开始遇到的问题就是 session 的存储了,解决方案大致有如下几种 使用 spring-session 它可以把 session 存储到你想存储的位置,如 redis,mysq ...

  6. python基础[18]——使用django创建一个简易的博客网站

    一.页面实现 index.html base.html post.html header.html footer.html <!-- index.html--> {% extends 'b ...

  7. 初识 Kafka Producer 生产者

    目录 1.KafkaProducer 概述 2.KafkaProducer 类图 3.KafkaProducer 简单示例 温馨提示:整个 Kafka Client 专栏基于 kafka-2.3.0 ...

  8. Spring||Mails

    JMail可以解决Java发送邮件,通过Jmail的核心javax.mail.jar实现,通过Jmail发送邮件需要经过以下步骤 1.创建解析邮件内容:Message类,通过javax.mail.in ...

  9. verilog HDL 进击之路

    Verilog 进击之路 - 夯实基础第一节之结构化设计 随着数字电路设计的复杂化和专业化,传统的电路设计逐渐没落,Verilog HDL逐渐走入历史舞台.好多人并不是不会Verilog,而是缺乏细致 ...

  10. 2020年我国到底有多少程序员?现在学习java还来得及吗?

    中国有多少程序员?现在还值得学java吗? 跪求关注,祝关注我的人都:身体健康,财源广进,福如东海,寿比南山,早上贵子,从不掉发! JAVA起于1995年,经过20多年的发展,JAVA如今已经发展成为 ...