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,表示无火的余灰的最小生命值。
Sample Input
5
1 2 2
2 3 2
3 4 2
1 4 1
4 5 4
Sample Output
8
样例说明
从2到5的路最危险,2 1 4 5受到了7点伤害,所以需要有8点生命值。
Data Constraint
Solution
-
由题知这是一颗环套树(n点n边,环大小 ≥ 1 \geq1 ≥1)。
-
题目要求的是这棵环套树的直径,再+1就是答案了。
-
我们把该环提出来,在上面操作。
-
那么直径要么是环上某点的子树内直径,要么是环上某两点子树内最长链再加一段环上路径。
-
子树内直径和最长链我们先算出来,剩下的就是如何求出环上两点使之匹配出来最大值。
-
枚举环上一点 i i i,同时维护一个指针 j j j 往后指,表示从 i + 1 i+1 i+1 到 j j j 是近一些,而 j j j 之后的点呢会绕环一圈来走回 i i i 更近一些。
-
那么 i + 1 i+1 i+1 到 j j j 的我们用一个单调队列来求最大值,绕一周的用一个后缀最大值算就好了。
-
时间复杂度 O ( n ) O(n) O(n) ,但常数有些大。
Code
#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e6+5;
int tot=1,top,qx,qy,node;
LL ans,mx;
bool pd;
int first[N],nex[N<<1],en[N<<1],w[N<<1];
int st[N],dfn[N],d[N],len[N],col[N],q[N];
LL suf[N],pre[N],f[N];
bool bz[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48),ch=getchar();
return w?-X:X;
}
inline LL max(LL x,LL y)
{
return x>y?x:y;
}
inline void insert(int x,int y,int z)
{
nex[++tot]=first[x];
first[x]=tot;
en[tot]=y;
w[tot]=z;
}
void dfs(int x,int y)
{
st[++top]=x;
dfn[x]=top;
for(int i=first[x];i && !pd;i=nex[i])
if(i^y)
{
if(dfn[en[i]])
{
for(int j=dfn[en[i]];j<=top;j++)
{
d[++d[0]]=st[j];
bz[st[j]]=true;
}
pd=true;
return;
}
dfs(en[i],i^1);
}
top--;
}
void find(int x,int y,LL z,LL &num)
{
if(z>num) num=z;
for(int i=first[x];i;i=nex[i])
if(en[i]^y && en[i]^x) find(en[i],x,z+w[i],num);
}
void get(int x,int y)
{
if(y==d[0]) return;
for(int i=first[x];i;i=nex[i])
if(en[i]==d[y+1])
{
len[y]=w[i];
get(en[i],y+1);
}
}
void dg(int x,int y,LL z)
{
if(z>mx) mx=z,node=x;
col[x]=tot;
for(int i=first[x];i;i=nex[i])
if(en[i]^y && en[i]^x && !bz[en[i]]) dg(en[i],x,z+w[i]);
}
void dg1(int x,int y,LL z)
{
if(z>mx) mx=z,node=x;
for(int i=first[x];i;i=nex[i])
if(en[i]^y && en[i]^x && col[en[i]]==tot) dg1(en[i],x,z+w[i]);
}
int main()
{
freopen("darksoul.in","r",stdin);
freopen("darksoul.out","w",stdout);
int n=read();
for(int i=1;i<=n;i++)
{
int x=read(),y=read(),z=read();
insert(x,y,z);
insert(y,x,z);
}
dfs(1,0);
tot=0;
for(int i=1;i<=d[0];i++)
{
for(int j=first[d[i]];j;j=nex[j])
if(!bz[en[j]]) find(en[j],d[i],w[j],f[i]);
node=mx=0;
tot++;
dg(d[i],0,0);
dg1(node,0,0);
ans=max(ans,mx);
if(f[i]>ans) ans=f[i];
}
get(d[1],1);
for(int i=first[d[d[0]]];i;i=nex[i])
if(en[i]==d[1])
{
len[d[0]]=w[i];
break;
}
for(int i=2;i<=d[0];i++) pre[i]=pre[i-1]+len[i-1];
for(int i=2;i<=d[0];i++) suf[i]=pre[d[0]]+len[d[0]]-pre[i]+f[i];
for(int i=d[0]-1;i>1;i--) suf[i]=max(suf[i+1],suf[i]);
int l=1,r=0;
for(int i=1,j=1;i<d[0];i++)
{
while(l<r && q[l]<=i) l++;
while(j<d[0] && pre[j+1]-pre[i]<=pre[d[0]]-pre[j+1]+len[d[0]]+pre[i])
{
j++;
while(l<=r && f[q[r]]+pre[q[r]]<=f[j]+pre[j]) r--;
q[++r]=j;
}
LL num=0;
if(j<d[0]) num=suf[j+1]+pre[i];
if(i<j) num=max(num,f[q[l]]+pre[q[l]]-pre[i]);
ans=max(ans,num+f[i]);
}
printf("%lld",ans+1);
return 0;
}