原文链接https://www.cnblogs.com/zhouzhendong/p/9246484.html

题目传送门 - Codeforces 1000G Two-Paths

题意

  给定一棵有 $n(2\leq n\leq 3\times 10^5)$ 个节点的树,其中节点 $i$ 有权值 $a_i$,边 $e$ 有权值 $w_e$。$(1\leq a_i,w_e\leq 10^9)$

  现在给出 $q(1\leq q\leq 4\times 10^5)$ 组询问,每组询问给定两个数 $x,y(1\leq x,y\leq n)$。

  如果一条路径的起点和终点分别为 $x$ 和 $y$,而且这条路径重复经过同一条边最多 $2$ 次(点可以多次经过),那么这条路径合法。

  一条合法路径 $P$ 的价值为 $Pr(p)$。$\text{Pr}(p) = \sum\limits_{v \in \text{distinct vertices in } p}{a_v} - \sum\limits_{e \in \text{distinct edges in } p}{k_e \cdot w_e}$。

  其中 $k_e$ 为路径 $p$ 经过 $e$ 的次数。

  每次询问问所有合法路径的最大价值。

题解

  我们来跑一下树形dp。

  求出以下值。(下面是示意图,有填色的部分表示被计算)

  其中 $dp1,dp2,f1,sum$ 都表示所取的部分的合法最大值。

  

    

  

  再描述一下上面六个量的意义:

  dp1[x]:往 $x$ 的后代节点走最多可以赚多少。

  dp2[x]:往 $x$ 的祖先走最多可以赚多少。

  f1[x]:从 $x$ 的祖先向 $x$ 走最多可以赚多少。

  sum[x]:令树根到 $x$ 父亲的链为主链,假设主链上行走没有任何消耗和获益,向主链走最多可以获益多少。

  len[x]:$x$ 到根的距离(带边权)。

  s[x]:$x$ 的深度。

  

  在回答询问 $x,y$ 的时候,首先从 $x$ 到 $y$ 的路径一定被选,其中的边一定只走一次。

  其他的我们根据之前维护的量分类讨论加加减减一下就可以了。

  详见代码。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=300005,M=N*2;
LL read(){
LL x=0;
char ch=getchar();
while (!('0'<=ch&&ch<='9'))
ch=getchar();
while ('0'<=ch&&ch<='9')
x=x*10+ch-48,ch=getchar();
return x;
}
struct Gragh{
int cnt,y[M],nxt[M],fst[N];
LL z[M];
void clear(){
cnt=0;
memset(fst,0,sizeof fst);
}
void add(int a,int b,LL c){
y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt;
}
}g;
int n,q,fa[N][20],depth[N],xx,yy;
LL a[N],dp1[N],f1[N],dp2[N],sum[N],fadis[N],len[N],s[N];
void dfs1(int x,int pre,int d,LL L){
fa[x][0]=pre;
depth[x]=d;
len[x]=L;
s[x]=s[pre]+a[x];
for (int i=1;i<20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
dp1[x]=0;
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
LL z=g.z[i];
if (y!=pre){
dfs1(y,x,d+1,L+z);
fadis[y]=z;
f1[y]=max(dp1[y]+a[y]-z*2,0LL);
dp1[x]+=f1[y];
}
}
}
void dfs2(int x,int pre,LL v,LL v2){
dp2[x]=v;
sum[x]=v2;
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
LL z=g.z[i];
if (y!=pre){
LL _v=max(v+a[x]+dp1[x]-f1[y]-2*z,0LL);
LL _v2=max(v2+dp1[x]-f1[y],0LL);
dfs2(y,x,_v,_v2);
}
}
}
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
for (int i=19;i>=0;i--)
if (depth[x]-(1<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
for (int i=19;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
xx=x,yy=y;
return fa[x][0];
}
int main(){
scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++)
a[i]=read();
g.clear();
for (int i=1;i<n;i++){
int a=read(),b=read();
LL c=read();
g.add(a,b,c);
g.add(b,a,c);
}
dfs1(1,0,0,0);
dfs2(1,0,0,0);
while (q--){
int x,y,lca;
scanf("%d%d",&x,&y);
if (depth[x]>depth[y])
swap(x,y);
lca=LCA(x,y);
if (x==lca){
if (y==lca){
printf("%I64d\n",dp1[x]+dp2[x]+a[x]);
continue;
}
LL ans=s[y]-s[x]+a[x];
ans-=len[y]-len[x];
ans+=sum[y]-sum[x];
ans+=dp2[x]+dp1[y];
printf("%I64d\n",ans);
continue;
}
LL ans=s[x]+s[y]-s[lca]*2+a[lca];
ans-=len[x]+len[y]-len[lca]*2;
ans+=sum[x]+sum[y]-sum[xx]-sum[yy];
ans+=dp1[lca]-f1[xx]-f1[yy];
ans+=dp2[lca]+dp1[x]+dp1[y];
printf("%I64d\n",ans);
}
return 0;
}

  

Codeforces 1000G Two-Paths 树形动态规划 LCA的更多相关文章

  1. 蓝桥杯 ALGO-4 结点选择 (树形动态规划)

    问题描述 有一棵 n 个节点的树,树上每个节点都有一个正整数权值.如果一个点被选择了,那么在树上和它相邻的点都不能被选择.求选出的点的权值和最大是多少? 输入格式 第一行包含一个整数 n . 接下来的 ...

  2. 树形动态规划(树状DP)小结

    树状动态规划定义 之所以这样命名树规,是因为树形DP的这一特殊性:没有环,dfs是不会重复,而且具有明显而又严格的层数关系.利用这一特性,我们可以很清晰地根据题目写出一个在树(型结构)上的记忆化搜索的 ...

  3. Codeforces 581F Zublicanes and Mumocrates - 树形动态规划

    It's election time in Berland. The favorites are of course parties of zublicanes and mumocrates. The ...

  4. Codeforces Beta Round #14 (Div. 2) D. Two Paths 树形dp

    D. Two Paths 题目连接: http://codeforces.com/contest/14/problem/D Description As you know, Bob's brother ...

  5. Codeforces 839C Journey - 树形动态规划 - 数学期望

    There are n cities and n - 1 roads in the Seven Kingdoms, each road connects two cities and we can r ...

  6. Codeforces Round #343 (Div. 2) E. Famil Door and Roads (树形dp,lca)

    Famil Door's City map looks like a tree (undirected connected acyclic graph) so other people call it ...

  7. LEETCODE —— Unique Paths II [动态规划 Dynamic Programming]

    唯一路径问题II Unique Paths II Follow up for "Unique Paths": Now consider if some obstacles are ...

  8. Codeforces Gym 100015C City Driving 离线LCA

    City Driving 题目连接: http://codeforces.com/gym/100015/attachments Description You recently started fre ...

  9. 【ACM/ICPC2013】树形动态规划专题

    前言:按照计划,昨天应该是完成树形DP7题和二分图.最大流基础专题,但是由于我智商实在拙计,一直在理解树形DP的思想,所以第二个专题只能顺延到今天了.但是昨天把树形DP弄了个5成懂我是很高兴的!下面我 ...

随机推荐

  1. Windows 下搭建 SVN服务器及使用

    目录 一 .安装Visual SVN 二.配置SVN 三.安装TortoiseSVN 四.上传项目到远程仓库 五.从远程仓库下载项目 六.检出项目 七.版本回退 参考链接 http://blog.cs ...

  2. 【原创】Linux基础之gz文件相关操作

    gz文件不需要解压即可进行相关操作 $ zcat test.log.gz $ zmore test.log.gz $ zless test.log.gz $ zgrep '1.2.3.4' test. ...

  3. Django 笔记(二) 新建 ~ 渲染

    新建APP python manange.py startapp app_name 然后右键 pycharm 的项目目录,将新建的目录从服务器上下载进来 URL(Uniform Resoure Loc ...

  4. 3)django-路由系统url

    一:django路由系统说明 路由都在urls文件里,它将浏览器输入的url映射到相应的业务处理逻辑 二:django 常用路由系统配置  1)URL常用有模式一FBV(function base v ...

  5. 2)实现github自动登陆获取信息

    # -*- coding:utf-8 -*- # __author__ = 'lixiang' # 实现github自动登陆和获取数据 import requests from bs4 import ...

  6. C#方法的重写

    问题一:什么是重写? “重写”父类方法就是修改它的实现方式或者说在子类中对它进行重新编写. 问题二:为什么要重写父类的方法      通常,子类继承父类的方法,在调用对象继承方法的时候,调用和执行的是 ...

  7. 任意N位二进制的补码实现——队列存放

    正在学习计算机组织与结构,为了写一些底层的算术操作模拟,比如一个二进制补码数的加减乘除,发现这很麻烦,因为不管是什么语言,都只提供了8位.32.64位等部分位数的补码形式,那么怎么实现任意任意位的补码 ...

  8. PID控制器开发笔记之一:PID算法原理及基本实现

    在自动控制中,PID及其衍生出来的算法是应用最广的算法之一.各个做自动控制的厂家基本都有会实现这一经典算法.我们在做项目的过程中,也时常会遇到类似的需求,所以就想实现这一算法以适用于更多的应用场景. ...

  9. Confluence 6 从一个备份中获得文件附件

    页面中的文件附件可以从备份中获得而不需要将备份文件导入到 Confluence 中.这个在用户删掉了附件,但是你还是想恢复这个附件的时候就变得非常有用了. 自动备份和手动备份都允许你进行这个操作,但是 ...

  10. Confluence 6 布局高级自定义

    重载 Velocity 模板 velocity 目录是 Confluence Velocity 模板文件进行搜索时候需要的文件夹.例如,你可以通过将你的 Velocity 文件使用正确的文件名放置到正 ...