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条边的无向连通图。”
根据这个性质,我们就可以认定这个图是一个刺球(图内只有一个环)。
就长这样的。
显然答案可能有两种情况:
- 某颗子树的直径
- 树内以环上点为端点的最长链与部分环组合
这样我们就可以随便乱搞了。
树上直径求法如果不会请自行百度……
在环上的组合可以用一个单调队列解决。
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;
}