【JZOJ A组】黑暗之魂(darksoul)

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

在这里插入图片描述

思路

显然上述方法无法拓展,原因在于将环套树看成图而不是一棵略复杂的树更简单的仙人掌。环套树可以视为将森林的根摆在一个环上的产物。显然,对每棵树我们可以计算出他的内部直径,之后只有这棵树的最长链有意义。问题规约为:求一个“刺球”的直径。暴力计算是O(k2)的(枚举所有的“刺”对)可以想到,这可以使用单调队列优化,我们要维护比圆的周长的一半要小的部分中的递减序列(因为 ans=max(len[x]+pre[x]-pre[i]-len[i]),枚举右端转移即可,要注意把序列倍长。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e6+11;
int n,cnt,k,bel,tot,head1,tail,boo,list[maxn],dfn[maxn],low[maxn],belong[maxn];
bool vis[maxn];
int root[maxn],q[maxn*2];
ll ans,f[maxn*2],len[maxn*2],pre[maxn*2],dis[maxn*2],mid;
struct E{ int v,to,from; }e[maxn*2];
struct A{ int x,y,v; }a[maxn];
vector<int>Q;
void insert(int a,int b,int c) 
{ 
	e[++cnt].to=b; e[cnt].from=list[a]; e[cnt].v=c; list[a]=cnt; 
	e[++cnt].to=a; e[cnt].from=list[b]; e[cnt].v=c; list[b]=cnt; 
}
void dfs(int x,int fa)
{
	ll max1=0,max2=0,l=0;
	for(int i=list[x]; i; i=e[i].from)
		if(belong[e[i].to]!=k&&e[i].to!=fa)
		{
			dfs(e[i].to,x);
			l=f[e[i].to]+e[i].v;
			if(l>max1) max2=max1,max1=l; else max2=max(l,max2);
		}
	ans=max(max1+max2,ans);
	f[x]=max1;
}
void tarjan(int x,int fa)
{
	dfn[x]=low[x]=++cnt,vis[x]=1;
	Q.push_back(x);
	for(int i=list[x]; i; i=e[i].from)
		if(e[i].to!=fa) 
		{
			if(!dfn[e[i].to])
			{
				tarjan(e[i].to,x);
				low[x]=min(low[x],low[e[i].to]);
			}
			else if(vis[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
		}
	if(low[x]==dfn[x])
	{
		bel++;
		int y=0,boo=0;
		if(Q.back()!=x) boo=1,k=bel;
		while(y!=x)
		{
			y=Q.back(),Q.pop_back();
			belong[y]=bel,vis[y]=0;
			if(boo) root[++tot]=y;
		}
	}
} 
bool cmp(A a,A b)
{
	return a.x==b.x?(a.y==b.y?a.v<b.v:a.y<b.y):a.x<b.x;
}
int main()
{
	freopen("darksoul.in","r",stdin); freopen("darksoul.out","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++) 
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
		if(a[i].x>a[i].y) swap(a[i].x,a[i].y);
	}
	sort(a+1,a+n+1,cmp);
	bool boo=false;
	for(int i=1; i<=n; i++)
		if(a[i].x==a[i].y||a[i].x==a[i-1].x&&a[i].y==a[i-1].y) boo=true;
		else insert(a[i].x,a[i].y,a[i].v);
	if(boo)
	{
		k=-1,dfs(1,0),printf("%lld",ans+1);
		return 0;
	}
	cnt=0,tarjan(1,0);
	for(int i=1; i<=tot; i++) dfs(root[i],0),len[i]=f[root[i]];
	for(int i=1; i<=tot; i++) 
		for(int j=list[root[i]]; j; j=e[j].from) 
			if(e[j].to==root[i!=1?i-1:tot])
			{
				dis[i]=(ll)e[j].v;
				break;
			}
	for(int i=1; i<=tot; i++) dis[i+tot]=dis[i],len[i+tot]=len[i];
	for(int i=1; i<=tot*2; i++) pre[i]=pre[i-1]+dis[i];
	mid=pre[tot];
	head1=tail=q[head1]=1;
	for(int i=2; i<=2*tot; i++)
	{
		while(head1<=tail&&(pre[i]-pre[q[head1]]>mid/2)) head1++;
		if(head1<=tail) ans=max(ans,len[q[head1]]+len[i]+pre[i]-pre[q[head1]]);
		while(head1<=tail&&(len[i]-pre[i]>=len[q[tail]]-pre[q[tail]])) tail--;
		q[++tail]=i;
	}
	printf("%lld",ans+1);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值