ICode9

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

小x游世界树

2021-09-04 23:04:25  阅读:154  来源: 互联网

标签:int 世界 魔法阵 ans 体力值 节点 dp


题目大意

传送中世界树是一棵非常巨大的树,它有着许许多多的枝条以及节点,每个节点上都有一个平台。

好不容易来到传说中的世界树下,小新当然要爬上去看看风景。

小新每经过一条边都会耗费体力值。然而世界树之主想给他弄些麻烦,于是他在每条边上都设了一个魔法阵,当小新踏上那条边时会被传送回根节点,魔法阵只生效一次。这岂不是要累死小新?幸运的是,每个平台上都有无数个加速器,这些加速器可以让小新在当前节点所连的边上耗费的体力值减少,不同平台的加速器性能不一定相同,但同一个平台的加速器性能绝对相同。世界树之主给了小新一次“换根”的机会,他可以将世界树的任何一个节点变为根,但所有的边都不能改变。

小新想问你,将根换为哪个节点能使小新爬到世界树上的每个节点耗费的体力值和最少。

默认编号为 \(1\) 的点为初始根。

对于 \(100\%\) 的数据,\(0<n<=700000\)

解题思路

比较显然的换根 DP

对于题目所说的魔法阵,一种可行的解释时,假设你要走到一个终点,再传送回根节点,你可以走到那个点时,把那个点通向的所有边都踩一下,再传回根节点,这样就不需要考虑魔法阵的问题了。

然后是换根 DP 的基本思路。

先求出以 \(1\) 为根时的总贡献。

然后将以点 \(1\) 为根时的总贡献传递到 \(1\) 的儿子中,然后一直传递。

设当前点为 \(u\),\(v\) 为 \(u\) 的儿子,\(dp_i\) 为以点 \(i\) 为根时的总贡献。

则有一下转移方程(自己画个图推一下就出来了):

\[dp_v = dp_u - siz_v * w_i + (siz_1 - siz_v) * w_{i \ xor \ 1} \]

上面的式子中,\(w_i\) 为 \(u\) 到 \(v\) 耗费的体力值,\(w_{i \ xor \ 1}\) 即为 \(v\) 到 \(u\) 耗费的体力值。(因为正向边与反向边的编号相差 \(1\),但要注意,编号要从 \(2\) 开始)。

AC CODE

#include <bits/stdc++.h>
#define int long long
#define _ (int) 2e6 + 5
using namespace std;

int n;

int ans = LLONG_MAX, id;

int a[_];

int tot = 1, head[_], to[_ << 1], nxt[_ << 1], w[_ << 1];

int dp[_], dep[_], path[_], cnt[_], kkk[_], siz[_];

void add(int u, int v, int val)
{
	to[++tot] = v;
	nxt[tot] = head[u];
	w[tot] = val;
	head[u] = tot;
}

void dfs(int u, int fa, int d)
{
	dep[u] = d;
	siz[u] = 1;
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i], dis = w[i];
		if(v == fa) continue;
		path[v] = path[u] + dis;
		dfs(v, u, d + 1);
		siz[u] += siz[v];
		dp[1] += path[v];
	}
}

void dfss(int u, int fa)
{
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(v == fa) continue;
		dp[v] = dp[u] -  siz[v] * w[i] + (siz[1] - siz[v]) * w[i xor 1];
		dfss(v, u);
	}
}

signed main()
{
	scanf("%lld", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
	}
	for(int i = 1; i < n; ++i)
	{
		int u, v, w;
		scanf("%lld%lld%lld", &u, &v, &w);
		add(u, v, w - a[u]);
		add(v, u, w - a[v]);
	}
	dfs(1, 0, 0);
	dfss(1, 0);
	for(int i = n; i >= 1; --i)
	{
		if(ans >= dp[i])
		{
			ans = dp[i];
			id = i;
		}
	}
	printf("%lld\n%lld\n", id, ans);
	return 0;
}

标签:int,世界,魔法阵,ans,体力值,节点,dp
来源: https://www.cnblogs.com/orzz/p/15228110.html

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

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

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

ICode9版权所有