题解

首先如果我们要确定出每个\(dis_{i \to i+1 , i \in odd}\)

这个可以用两遍树形\(DP\)来解决

一遍是考虑走子树子树绕过来的

一遍是考虑从走祖先绕过来的

然后就可以考虑用倍增来解决了

设\(st1[u][i][0/1][0/1]\)表示从点\(u\)开始向上跳\(2^j\)步,开始的位置位于左/右边的树,结束的位置位于左/右边的树

倍增的时候就用两个数组\(dp1[0/1]/dp2[0/1]\)表示从\(u/v\)到左/右边树的\(LCA\)

不断往上跳着更新即可

注意要先把\(dp1/dp2\)的数组的值记录下来再用记录的值更新==

代码

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 300005 ;
const LL INF = 1e18 ;
using namespace std ; inline LL read() {
char c = getchar() ; LL x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
} int n , lg[M] , dep[M] ;
LL w[M] , st[M][22] , st1[M][22][2][2] , dp1[2] , dp2[2] ;
struct Node { int v ; LL w1 , w2 ; } ;
vector < Node > vec[M] ;
void dfs1(int u , int father) {
LL w1 , w2 ;
st[u][0] = father ; dep[u] = dep[father] + 1 ;
for(int i = 0 , v ; i < vec[u].size() ; i ++) {
v = vec[u][i].v , w1 = vec[u][i].w1 , w2 = vec[u][i].w2 ;
if(v == father) continue ; dfs1(v , u) ;
w[u] = min( w[u] , w[v] + w1 + w2 ) ;
}
}
void dfs2(int u , int father) {
LL w1 , w2 ;
for(int i = 0 , v ; i < vec[u].size() ; i ++) {
v = vec[u][i].v , w1 = vec[u][i].w1 , w2 = vec[u][i].w2 ;
if(v == father) continue ;
w[v] = min( w[v] , w[u] + w1 + w2 ) ;
dfs2(v , u) ;
}
}
void dfs(int u , int father) {
LL w1 , w2 ;
for(int i = 0 , v ; i < vec[u].size() ; i ++) {
v = vec[u][i].v , w1 = vec[u][i].w1 , w2 = vec[u][i].w2 ;
if(v == father) continue ;
dfs(v , u) ;
st1[v][0][0][0] = min( w1 , w[v] + w2 + w[u] ) ;
st1[v][0][1][1] = min( w2 , w[u] + w1 + w[v] ) ;
st1[v][0][0][1] = min( w[v] + w2 , w1 + w[u] ) ;
st1[v][0][1][0] = min( w2 + w[u] , w[v] + w1 ) ;
}
}
inline int LCA(int u , int v) {
if(dep[u] < dep[v]) swap(u , v) ;
for(int i = lg[n] ; i >= 0 ; i --) if(dep[st[u][i]] >= dep[v]) u = st[u][i] ;
if(u == v) return u ;
for(int i = lg[n] ; i >= 0 ; i --) if(st[u][i] != st[v][i]) u = st[u][i] , v = st[v][i] ;
return st[u][0] ;
}
inline LL Solve(int x , int y) {
int u = ((x + 1) >> 1) , v = ((y + 1) >> 1) , lcap ;
if(dep[u] < dep[v]) {
swap(u , v) ;
swap(x , y) ;
}
lcap = LCA(u , v) ;
LL tp0 , tp1 ;
dp1[(x + 1) & 1] = 0 ; dp1[((x + 1) & 1) ^ 1] = w[u] ;
for(int i = lg[n] ; i >= 0 ; i --) {
if(dep[st[u][i]] >= dep[lcap]) {
tp0 = dp1[0] ; tp1 = dp1[1] ;
dp1[0] = min( tp0 + st1[u][i][0][0] , tp1 + st1[u][i][1][0] ) ;
dp1[1] = min( tp0 + st1[u][i][0][1] , tp1 + st1[u][i][1][1] ) ;
u = st[u][i] ;
}
}
u = v ;
dp2[(y + 1) & 1] = 0 ; dp2[((y + 1) & 1) ^ 1] = w[u] ;
for(int i = lg[n] ; i >= 0 ; i --) {
if(dep[st[u][i]] >= dep[lcap]) {
tp0 = dp2[0] , tp1 = dp2[1] ;
dp2[0] = min( tp0 + st1[u][i][0][0] , tp1 + st1[u][i][1][0] ) ;
dp2[1] = min( tp0 + st1[u][i][0][1] , tp1 + st1[u][i][1][1] ) ;
u = st[u][i] ;
}
}
return min(dp1[0] + dp2[0] , dp1[1] + dp2[1]) ;
}
int main() {
n = read() ;
for(int i = 2 ; i <= n ; i ++) lg[i] = lg[i >> 1] + 1 ;
for(int i = 1 ; i <= n ; i ++) w[i] = read() ;
LL w1 , w2 ;
for(int i = 1 , u , v ; i < n ; i ++) {
u = read() ; v = read() ; w1 = read() ; w2 = read() ;
vec[u].push_back( (Node) { v , w1 , w2 } ) ;
vec[v].push_back( (Node) { u , w1 , w2 } ) ;
}
memset(st1 , 31 , sizeof(st1)) ;
dfs1(1 , 0) ; dfs2(1 , 0) ; dfs(1 , 0) ;
for(int j = 1 ; j <= lg[n] ; j ++)
for(int u = 1 ; u <= n ; u ++) {
st[u][j] = st[st[u][j - 1]][j - 1] ;
st[u][j] = st[st[u][j - 1]][j - 1] ;
st1[u][j][0][0] = min( st1[u][j - 1][0][0] + st1[st[u][j - 1]][j - 1][0][0] , st1[u][j - 1][0][1] + st1[st[u][j - 1]][j - 1][1][0] ) ;
st1[u][j][0][1] = min( st1[u][j - 1][0][0] + st1[st[u][j - 1]][j - 1][0][1] , st1[u][j - 1][0][1] + st1[st[u][j - 1]][j - 1][1][1] ) ;
st1[u][j][1][0] = min( st1[u][j - 1][1][0] + st1[st[u][j - 1]][j - 1][0][0] , st1[u][j - 1][1][1] + st1[st[u][j - 1]][j - 1][1][0] ) ;
st1[u][j][1][1] = min( st1[u][j - 1][1][0] + st1[st[u][j - 1]][j - 1][0][1] , st1[u][j - 1][1][1] + st1[st[u][j - 1]][j - 1][1][1] ) ;
}
int Q = read() , x , y ;
while(Q --) {
x = read() ; y = read() ;
printf("%lld\n",Solve(x , y)) ;
}
return 0 ;
}

CF1140G Double Tree的更多相关文章

  1. Codeforces 1140G Double Tree 倍增 + dp

    刚开始, 我以为两个点肯定是通过树上最短路径过去的, 无非是在两棵树之间来回切换, 这个可以用倍增 + dp 去维护它. 但是后来又发现, 它可以不通过树上最短路径过去, 我们考虑这样一种情况, 起点 ...

  2. @hdu - 6594@ Double Tree

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定两棵 N 个点的树,以及树上每条边的权值 w(u, v),每 ...

  3. Codeforces 68D - Half-decay Tree

    题意 有一颗高度为 \(h\) 的完全二叉树(即点数为 \(2^{h+1}-1\) ),有两种操作: add x y 给 \(x\) 点的权值加 \(y\) decay 一次衰变定义为选择一个叶子节点 ...

  4. 平衡树 替罪羊树(Scapegoat Tree)

    替罪羊树(Scapegoat Tree) 入门模板题 洛谷oj P3369 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入xx数 删除xx数(若有多个相同 ...

  5. Color a Tree

    题目链接:Click here Solution: 看起来不太能dp,则考虑树上贪心 题目要求一个点必须先染父亲才能染自己,就给了我们启示,贪心的合并点 我们定义一个点的权重为这个点的价值和/点数,然 ...

  6. CodeForces 602E【概率DP】【树状数组优化】

    题意:有n个人进行m次比赛,每次比赛有一个排名,最后的排名是把所有排名都加起来然后找到比自己的分数绝对小的人数加一就是最终排名. 给了其中一个人的所有比赛的名次.求这个人最终排名的期望. 思路: 渣渣 ...

  7. Educational Codeforces Round 62 (Rated for Div. 2) Solution

    最近省队前联考被杭二成七南外什么的吊锤得布星,拿一场Div. 2恢复信心 然后Div.2 Rk3.Div. 1+Div. 2 Rk9,rating大涨200引起舒适 现在的Div. 2都怎么了,最难题 ...

  8. poj 1151 (未完成) 扫描线 线段树 离散化

    #include<iostream> #include<vector> #include<cmath> #include<algorithm> usin ...

  9. codeforce 139E

    成段更新+离散化才能过,数据好强.. 单点更新挂在了test27,下次做到成段更新再来做! /* 期望=存活概率*点权值/100 ans=sum(期望) 离散化树木权值,数轴统计累加可能倒下的树木概率 ...

随机推荐

  1. topcoder srm 552

    div1 250pt: 题意:用RGB三种颜色的球摆N层的三角形,要求相邻的不同色,给出RGB的数量,问最多能摆几个 解法:三种颜色的数量要么是全一样,要么是两个一样,另外一个比他们多一个,于是可以分 ...

  2. md5sum使用注意事项

    1. linux 命令行的 md5sum命令 echo -n "123456" | md5sum     //echo的时候, 默认是自带回车的, 必须要去掉 -n 去掉回车. 2 ...

  3. org.hibernate.AnnotationException: No identifier specified for entity:

    使用hibernate的e-r映射pojo类的时候遇到org.hibernate.AnnotationException: No identifier specified for entity的异常, ...

  4. Office EXCEL 如何保留两位小数,四舍五入

    选中若干单元格,然后右击设置单元格格式,数值中保留两位小数   使用round函数四舍五入,如下图所示,我在N10单元格中输入"ROUND(M10,1)"即可,其中ROUND是函数 ...

  5. ubuntu 安装后要做的事情

    1. 安装chrome,软件中心就可以. 2. 安装vim 和一些插件.这里引入一大牛配置的插件集 sudo apt-get install vim-gtk wget -qO- https://raw ...

  6. c++代码赏析之类对象传參

    #include <iostream> using namespace std; class A { private: int x; public: A():x(0) { x = 0; c ...

  7. Android项目之HomeHealth基础学习2:Service

    一. Service简单介绍 Service是android 系统中的四大组件之中的一个(Activity.Service.BroadcastReceiver.ContentProvider),它跟A ...

  8. 每天复习Shell—ls

    ls命令是linux下最经常使用的命令.ls命令就是list的缩写缺省下ls用来打印出当前文件夹的清单假设ls指定其它文件夹那么就会显示指定文件夹里的文件及文件夹清单. 通过ls 命令不仅能够查 ...

  9. HDU4738 Caocao's Bridges —— 边双联通分量 + 重边

    题目链接:https://vjudge.net/problem/HDU-4738 A network administrator manages a large network. The networ ...

  10. YTU 2858: 编程题AB-骨牌铺方格

    2858: 编程题AB-骨牌铺方格 时间限制: 1 Sec  内存限制: 128 MB 提交: 87  解决: 23 题目描述 在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出 ...