题目大意:给你一棵树,点有点权$a_{i}$,边有边权$w_{e}$,定义一种路径称为$2-path$,每条边最多经过2次且该路径的权值为$\sum _{x} a_{x}\;-\;\sum_{e}w_{e}\cdot k_{e}$,$k_{e}$为边的经过次数,一共$Q$次询问,每次查询经过$x,y$的$2-path$权值最大的路径的权值

看题解之前感觉不可做...有点思路但感觉不靠谱,都被我否掉了

LiGuanlin神犇提供了一种树形DP的解法

定义$f[x][0]$表示该子树内,经过$x$点的$2path$路径的最大权值$-a[x]$的值

$f[x][1]$表示x点父树的子树内,不经过$x$点的路径的最大权值

$f[x][2]$表示该节点从父节点过来的$2path$路径的最大权值

$dfs$2次搜出$f$,再维护前缀和,转移即可

注意当倍增$lca$来找到$lca(x,y)$最靠近$x$的儿子,如果它在$f[lca(x,y)]$最优状态里,需要去掉它个贡献

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define N 301000
#define uint unsigned int
#define ll long long
#define mod 1000000007
using namespace std; int gint()
{
int ret=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*f;
}
int n,q,cte;
int a[N],fe[N],fa[N],head[N];
ll f[N][],g[N][],vsum[N],esum[N];
struct Edge{int to,nxt,val;}edge[N*];
void ae(int u,int v,int w){
cte++;edge[cte].to=v,edge[cte].val=w;
edge[cte].nxt=head[u],head[u]=cte;
}
int use[N],dep[N];
ll dis[N],sum[N];
int ff[N][];
void dfs1(int u,int dad)
{
ff[u][]=u;
for(int j=head[u];j;j=edge[j].nxt){
int v=edge[j].to;
if(v==dad) continue;
dep[v]=dep[u]+,fa[v]=u,ff[v][]=u,fe[v]=edge[j].val;
dis[v]=dis[u]+edge[j].val;
vsum[v]=vsum[u]+a[v];
esum[v]=esum[u]+edge[j].val;
dfs1(v,u);
if(f[v][]+a[v]-2ll*edge[j].val>)
use[v]=,f[u][]+=f[v][]+a[v]-2ll*edge[j].val;
}
}
void dfs2(int u)
{
for(int j=head[u];j;j=edge[j].nxt){
int v=edge[j].to;
if(v==fa[u]) continue;
if(use[v]){
f[v][]=f[u][]-(f[v][]+a[v]-2ll*edge[j].val);
f[v][]=max(0ll,f[u][]+f[v][]+a[u]-2ll*edge[j].val);
}else{
f[v][]=f[u][];
f[v][]=max(0ll,f[u][]+f[v][]+a[u]-2ll*edge[j].val);
}
g[v][]=g[u][]+f[v][];
g[v][]=g[u][]+f[v][];
dfs2(v);
}
}
void get_lca()
{
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
ff[i][j]=ff[ ff[i][j-] ][j-];
}
int Lca(int x,int y,int &fx,int &fy)
{
int px=x,py=y,ans,dx,dy,flag=;
if(dep[x]<dep[y]) swap(x,y);
for(int i=;i>=;i--)
if(dep[ff[x][i]]>=dep[y]) x=ff[x][i];
for(int i=;i>=;i--)
if(ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i];
else ans=ff[x][i];
x=px,y=py;
dx=dep[ans]+;
dy=dep[ans]+;
for(int i=;i>=;i--){
if(dep[ff[x][i]]>=dx) x=ff[x][i];
if(dep[ff[y][i]]>=dy) y=ff[y][i];
}fx=x,fy=y;
return ans;
}
ll solve(int x,int y)
{
if(x==y) return f[x][]+f[x][]+a[x];
else if(fa[x]==y) return f[x][]+f[x][]+f[y][]+a[x]+a[y]-fe[x];
else if(fa[y]==x) return f[y][]+f[y][]+f[x][]+a[x]+a[y]-fe[y];
int fx,fy,lca;
lca=Lca(x,y,fx,fy);
ll ans=;
ans+=(vsum[x]+vsum[y]-vsum[lca]-vsum[fa[lca]])-(esum[x]+esum[y]-2ll*esum[lca]);
ans+=f[x][]+f[y][];
if(y==fy&&x!=fx){
ans+=g[x][]+g[y][]-2ll*g[lca][]-f[fx][];
if(use[fx]) ans-=f[fx][]+a[fx]-2ll*fe[fx];
}else{
ans+=g[x][]+g[y][]-2ll*g[lca][]-f[fy][];
if(use[fy]) ans-=f[fy][]+a[fy]-2ll*fe[fy];
}
ans+=f[lca][];
return ans;
} int main()
{
scanf("%d%d",&n,&q);
int x,y,w;
for(int i=;i<=n;i++)
a[i]=gint();
for(int i=;i<n;i++)
x=gint(),y=gint(),w=gint(),ae(x,y,w),ae(y,x,w);
dep[]=;
vsum[]=a[],dfs1(,-);
g[][]=f[][],dfs2();
get_lca();
for(int i=;i<=q;i++)
x=gint(),y=gint(),printf("%lld\n",solve(x,y));
return ;
}

CF1000G Two-Paths (树形DP)的更多相关文章

  1. 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 ...

  2. SPOJ6717 Two Paths 树形dp

    首先有朴素的\(O(n^2)\)想法 首先枚举断边,之后对于断边之后的两棵子树求出直径 考虑优化这个朴素的想法 考虑换根\(dp\) 具体而言,首先求出\(f[i], fs[i]\)表示\(i\)号点 ...

  3. 牛客第八场 C-counting paths 树形dp计数

    题目地址 题意 给你一颗树 初始点颜色全部为白色 对于每一个满足要求一的点集s f(s)的定义为先把点集内的点染黑 满足要求二的路径集合数量 要求一为两两黑点之间不能出现白色的点 要求二为将这个路径集 ...

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

    Two Paths time limit per test 2 seconds memory limit per test 64 megabytes input standard input outp ...

  5. HDU4003Find Metal Mineral[树形DP 分组背包]

    Find Metal Mineral Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Other ...

  6. CF 337D Book of Evil 树形DP 好题

    Paladin Manao caught the trail of the ancient Book of Evil in a swampy area. This area contains n se ...

  7. hdu 4003 Find Metal Mineral 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal miner ...

  8. poj3162(树形dp+优先队列)

    Walking Race Time Limit: 10000MS   Memory Limit: 131072K Total Submissions: 5409   Accepted: 1371 Ca ...

  9. POJ 3162.Walking Race 树形dp 树的直径

    Walking Race Time Limit: 10000MS   Memory Limit: 131072K Total Submissions: 4123   Accepted: 1029 Ca ...

随机推荐

  1. Mac Technology Overview

    [Mac Technology Overview]https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual ...

  2. spring-session与redis实现session共享

    一.准备 两个不同端口号的输出request.getLocalPort() + " ---> " + request.getSession(true).getId()的简单S ...

  3. day19-2 生成器,递归函数

    目录 生成器 有关yield的理解 实现range()函数 生成器表达式 递归 思考 斐波那契额 汉诺塔 二分法 生成器 自定义的迭代器 yield关键字: 和return一样,接收值,但不终止函数 ...

  4. SVG矢量动画

    一.概述 相较于png.jpg等位图通过存储像素点来记录图像,svg (Scalable Vector Graphics)拥有一套自己的语法,通过描述的形式来记录图形.Android并不直接使用原始的 ...

  5. css——外部样式

    外部样式 先建立一个css文件,如下: 然后开始写代码,不要加<style> 然后可以在html文件中的<head>内引用:<link rel="stylesh ...

  6. 根据 thread id 停止一个线程

    出自 https://github.com/Bogdanp/dramatiq/blob/master/dramatiq/middleware/threading.py#L62 thread_id = ...

  7. 斯特林公式--取N阶乘近似值

    斯特林公式(Stirling's approximation)是一条用来取n的阶乘的近似值的数学公式.一般来说,当n很大的时候,n阶乘的计算量十分大,所以斯特林公式十分好用,而且,即使在n很小的时候, ...

  8. [luogu2501 HAOI2006] 数字序列 (递推LIS)

    题目描述 现在我们有一个长度为n的整数序列A.但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列.但是不希望改变过多的数,也不希望改变的幅度太大. 输入输出格式 输入格式: 第一行包含一个数 ...

  9. python 递归算阶乘 (转载)

    Python 递归函数 递归函数在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 ...

  10. Docker决战到底(三) Rancher2.x的安装与使用 - 简书

    原文:Docker决战到底(三) Rancher2.x的安装与使用 - 简书   image.png 当越来越多的容器化应用被部署,一个可以管理编排这些容器的工具此时就显得尤为重要了.目前容器编排领域 ...