October 15th 模拟赛A T2 NOIP2018五校联考 黑暗之魂 Solution

37 篇文章 0 订阅
6 篇文章 0 订阅

题目空降

Description

oi_juruo热爱一款名叫黑暗之魂的游戏。在这个游戏中玩家要操纵一名有 点生命值的无火的余灰在一张地图中探险。地图中有n个篝火(也就是存档点)。在篝火处休息可以将生命值恢复满。每个篝火都会向其他篝火的其中之一连有一条通道(显然,通道是双向的),这些篝火之间都相互可达。也就是说,这是一张n个点,n条边的无向连通图。每条通道里都有一些怪物,经过oi_juruo的分析,他得到了每条边的怪物会对他造成的伤害值 .为了向oier们表演他高超的游戏技巧,他要从任意一个篝火跑到任意另一个篝火而不在之间的篝火休息,在此期间,他会和他经过的通道中的怪物战斗并损失 的生命值。现在oi_juruo想知道,他的生命值 至少为多少,才能完成任意一条旅途。oi_juruo并不傻,他会走最安全的路。本题时限为3000ms

Input

第一行一个整数n。之后n行,每行三个整数ui,vi,ai ,表示有一条从ui 连向vi ,怪物伤害值为ai 的通道。

Output

一行一个数hp,表示无火的余灰的最小生命值。

Analysis

这题有个非常重要的条件,就是 “这是一张n个点,n条边的无向连通图。”
根据这个性质,我们就可以认定这个图是一个刺球(图内只有一个环)。
在这里插入图片描述
就长这样的。
显然答案可能有两种情况:

  1. 某颗子树的直径
  2. 树内以环上点为端点的最长链与部分环组合

这样我们就可以随便乱搞了。
树上直径求法如果不会请自行百度……
在环上的组合可以用一个单调队列解决。

P.S.

不开LL见祖宗。
不开LL见祖宗。
不开LL见祖宗。

Code

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;

const long long MaxN = 1E6;

struct node
{
	long long v, w, next;
	node() {}
	node(long long _v, long long _w, long long _next) {v = _v, w = _w, next = _next;}
};

long long n;
node d[MaxN * 2 + 5];
long long final[MaxN + 5], cnt = 1;
long long deg[MaxN + 5];
bool bj[MaxN + 5];
long long c[MaxN * 2 + 5], dis[MaxN * 2 + 5], len;
long long pos, maxi;
long long pre[MaxN + 5];
queue <long long> q;
long long s[MaxN + 5];
long long ans;

void Link(long long u, long long v, long long w)
{
	d[++cnt] = node(v, w, final[u]), final[u] = cnt;
	d[++cnt] = node(u, w, final[v]), final[v] = cnt;
}

long long Dfs(long long u, long long w, long long lst)
{
	bj[u] = false;

	if (w > maxi)
		maxi = w, pos = u;

	for (long long i = final[u]; i; i = d[i].next)
	{
		long long v = d[i].v;

		if (!bj[v])
			continue;

		Dfs(v, w + d[i].w, i);
	}

	bj[u] = true;
}

int main(int argc, char const *argv[])
{
	freopen("init.in", "r", stdin);
	//freopen("darksoul.out", "w", stdout);
	scanf("%lld", &n);

	for (long long i = 1; i <= n; i++)
	{
		long long u, v, w;
		scanf("%lld%lld%lld", &u, &v, &w);
		deg[u]++, deg[v]++;
		len += w;
		Link(u, v, w);
	}

	for (long long i = 1; i <= n; i++)
		if (deg[i] == 1)
			q.push(i), bj[i] = true;

	c[0] = n;

	for (; !q.empty(); q.pop())
	{
		c[0]--;
		long long u = q.front(), v;

		for (long long i = final[u]; i; i = d[i].next)
		{
			v = d[i].v;

			if (bj[v])
				continue;

			bj[v] = true, deg[v]--;
			len -= d[i].w;

			if (deg[v] == 1)
				q.push(v);
			else
			{
				c[1] = v;
				maxi = 0;
				Dfs(v, 0, 0);
				pre[v] = maxi;
				maxi = 0;
				Dfs(pos, 0, 0);
				ans = max(maxi, ans);
				bj[v] = false;
			}
		}
	}

	for (long long i = 1; i < c[0]; i++)
	{
		bj[c[i]] = true;

		for (long long j = final[c[i]]; j; j = d[j].next)
			if (!bj[d[j].v])
			{
				c[i + 1] = c[c[0] + i + 1] = d[j].v, dis[i + 1] = dis[c[0] + i + 1] = d[j].w;
				break;
			}
	}

	c[c[0] + 1] = c[1];

	for (long long j = final[c[c[0]]]; j; j = d[j].next)
		if (d[j].v == c[1])
		{
			dis[c[0] + 1] = d[j].w;
			break;
		}

	for (long long i = 1; i <= c[0] * 2; i++)
		dis[i] += dis[i - 1];

	for (long long i = 1, j = 2, l = 1, r = 0; i <= c[0]; i++)
	{
		for (; l <= r && i >= s[l]; l++);

		for (; dis[j] - dis[i] <= len / 2; j++)
		{
			for (long long tmp = s[r]; l <= r && dis[tmp] + pre[c[tmp]] < dis[j] + pre[c[j]]; tmp = s[--r]);

			s[++r] = j;
		}

		long long tmp = s[l];
		ans = max(ans, dis[tmp] + pre[c[tmp]] - dis[i] + pre[c[i]]);
	}

	printf("%lld\n", ans + 1);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值