ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1000G Two-Paths 题解

2021-07-25 09:31:38  阅读:137  来源: 互联网

标签:tmp Paths 遍历 int 题解 CF1000G edge LCA dp


CF1000G Two-Paths 题解

题意

给定一颗树,询问一条以 u,v 为起点与重点的路径的 点权和-边权和,
每条边最多经过两次,点权仅能算一次所能得到的最大值。

思路:换根DP

首先对于上面这个图,我们可以发现整个路径的特征,边的编号表示一种可能的遍历顺序

1.(其实可以先遍历 u 的子树,只不过图中未画出。)

2.在 uLCA 的路径上的一点 x ,可以选择遍历完 x 的子树后再往 x 的父亲走。

3.到达了 LCA 后,可以选择遍历完 LCA 的子树后再遍历 LCA 向上的那一部分。

4.从 LCAv 的路径上一点 x 可以选择遍历完 x 除了向 v 的那一部分在向 v 那边走.

于是设一个dp 方程 dp[x][0]dp[x][1]

dp[x][0] 表示从 x 往下走最终回到 x 的最大价值。

dp[x][1] 表示从 x 随便走(即以 x 为出发点的全局最大值),最终回到 x 的最大价值。

PS:上文的遍历是在最优选择下的遍历,并不是全部遍历完的意思。

DP的转移:

仅仅选择子树的话很简单,设当前点为 u , 出点为 v

如果当前 u 最优可以选择 v ,一定是可以获得的价值 >0 ,即 dp[v][0]-edge[i]-edge[i^1]edge[i] 表示这条边,而edge[i^1] 就是这条边的反边。(因为过去还要回来,所以是这个代价)

于是方程也很简单:dp[u][0] 的初始值首先设为 a[x] 然后 dp[x][0]+=max(0,dp[y][0]-edge[i]-edge[i^1])

也就是一个简单的 Dfs 可以解决。

dp[u][1] 仅仅需要一个换根就解决了。

但是注意转移时,因为 dp[u][1] 表示的是全局,可能会对 dp[v][1] 产生重复计算,这个时候我们需要剪掉重复计算的部分,即 dp[v][0] ,当然,dp[v][0] 可能对 dp[u][1] 无贡献。计算一下是否 dp[v][0]-edge[i]-edge[i^1] 是否>0即可,转移时同样计算 dp[u][1] 是否可以对它的产生贡献,也就是除去重复的贡献后再剪掉两个边的边权,距离可以看代码。

两个 Dfs 即可搞定,一次预处理,一次换根就可以了。

处理答案

首先发现 u->LCA->v 是必定经过的,所以预先处理出这个边权,

h(u,v)v 是否计入 u 的贡献,即 \(\max(0,dp[v][0]-e_{u->v}-e_{v->u})\)

发现整个 u->LCA 的代价可以表示为 \(\sum_{i=1}^n dp[u_i][0]-h(fa_{u_i},u_i)\) (这个式子的意思就是选完路径上每一个点的最优子树然后剪掉重复贡献。

同理的v->LCA 的代价也珂以用一个类似的式子表示,最后加上 \(dp[LCA][1]\)​​ 再减去路径边权即可。

然后上面的那个式子可以用一个前缀和优化掉。这样就可以直接求出答案了。

用了树剖求 LCA,于是处理路径的边权和会包含在了树剖中的 DFS1 里面。

Code:

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch))	f|=ch=='-',ch=getchar();
	while(isdigit(ch))	x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	x=f?-x:x;return;
}
template <typename T>
inline void print(T x){
	if(x<0)	putchar('-'),x=-x;
	if(x>9)	print(x/10);
	putchar(x%10^48);return;
}
#define int long long
const int N=3e5+3;
int head[N],to[N<<1],Next[N<<1],edge[N<<1],tot=1,n,a[N],q;
inline void Addedge(int u,int v,int w){
	to[++tot]=v,edge[tot]=w,Next[tot]=head[u],head[u]=tot;
	return;
}
//Tree&DP
int fa[N],siz[N],son[N],dep[N],up[N],down[N];
inline void Dfs1(int x,int f){
	dep[x]=dep[f]+1,siz[x]=1,fa[x]=f;
	for(register int i=head[x];i;i=Next[i]){
		int y=to[i];if(y==f)	continue;
		down[y]=down[x]+edge[i],up[y]=up[x]+edge[i^1];
		Dfs1(y,x);siz[x]+=siz[y];
		son[x]=siz[son[x]]<siz[y] ? y :son[x];
	}
	return;
}
int top[N];
inline void Dfs2(int x){
	top[x]=son[fa[x]]==x ? top[fa[x]] : x;
	if(son[x])	Dfs2(son[x]);
	for(register int i=head[x];i;i=Next[i]){
		int y=to[i];
		if(y==fa[x]||y==son[x])	continue;
		Dfs2(y);
	}
	return;
}
inline int LCA(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
		else v=fa[top[v]];
	}
	return dep[u]>dep[v] ? v: u;
}
//DP
int dp[N][2];
inline int Get(int v,int i){return dp[v][0]-edge[i]-edge[i^1];}
int g[N];
inline void Dfs(int x){
	dp[x][0]=a[x];
	for(register int i=head[x];i;i=Next[i]){
		int y=to[i];
		if(y==fa[x])	continue;
		Dfs(y);
		if(Get(y,i)>0)	dp[x][0]+=Get(y,i);
	}
	return;
}
inline void Croot(int x){
	for(register int i=head[x];i;i=Next[i]){
		int y=to[i];
		if(y==fa[x])	continue;
		int tmp=0;if(Get(y,i)>0)	tmp=Get(y,i);
		if(dp[x][1]-tmp-edge[i]-edge[i^1]>0) dp[y][1]=dp[y][0]+dp[x][1]-tmp-edge[i]-edge[i^1];
		else dp[y][1]=dp[y][0];
		g[y]=g[x]+dp[y][0]-tmp;
		Croot(y);
	}
	return;
}

signed main(){
	read(n),read(q);
	for(register int i=1;i<=n;++i)	read(a[i]);
	for(register int i=1;i<n;++i){
		int x,y,z1,z2;read(x),read(y),read(z1),z2=z1;
		Addedge(x,y,z1),Addedge(y,x,z2);
	}
	Dfs1(1,0),Dfs2(1);
	Dfs(1);dp[1][1]=dp[1][0];Croot(1);
	while(q--){
		int u,v;read(u),read(v);
		int l=LCA(u,v);
		int ans1=up[u]-up[l]+down[v]-down[l],ans2=g[u]+g[v]-2*g[l]+dp[l][1];
		print(ans2-ans1),putchar('\n');
	}
	return 0;
}

标签:tmp,Paths,遍历,int,题解,CF1000G,edge,LCA,dp
来源: https://www.cnblogs.com/NuoCarter/p/15057077.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有