怪物

【题目背景】 很久很久以前 巨龙突然出现 带来灾难 带走啦公主又消失不见 【问题描述】 你在玩一个 RPG 类游戏,作为一名叫“达拉崩吧”的勇者,你需要打败巨龙来拯救公 主。 有一关中,有 n 座岛屿,m 座桥,每座桥连通两座岛屿,桥上会有一些敌人,玩家只 有消灭了桥上的敌人才能通过,与此同时桥上的敌人会对玩家造成一定伤害。 而且会有一个战斗力高达 9999999999(该数字与题目无关)的名叫“昆特牌提琴烤 蛋挞苏打马拉松”的巨龙镇守一座桥,以勇者目前的能力,是不可能通过的。 而巨龙是狡猾的,巨龙会镇守某一座桥,使得勇者受到最多的伤害才能从岛屿 1 到达 岛屿 n。当然,勇者会选择受到伤害最小的路径。 问,勇者要受到的伤害是多少。 【输入格式】 第一行两个整数 n; m。 接下来 m 行,每行三个整数 s; t; c,表示一座连接岛屿 s 和 t 的桥上的敌人会对勇者造 成 c 的伤害。 【输出格式】 一行,一个整数 d。d 表示勇者要受到的伤害是多少。 【样例输入】 3 4 1 2 1 1 2 2 2 3 1 2 3 2 【样例输出】 3 【数据范围】 100%的数据, 1 ≤ n ≤ 100000, 1 ≤ m ≤ 200000, 1 ≤ c ≤ 10000,数据保证任意删去 一条边后勇者可以从岛屿 1 到达岛屿 n

1-5:枚举删去哪一条边,跑一遍最短路。
6-8:跑出所有可能的路径,一一尝试一遍。
100%做法:
我们先从1为起点、从n为起点跑两边dij,获得每一个点到起点1、终点n的最短距离,其实距离和边权之间的关系相当于构建了由1为根的和由n为根的最短路树---所有最短路组成的树。
不难发现,要删除一条边,并使得最短路增大,一定要删除最短路上的边。所以我们找到从1到n的一条最短路链。找的方法就是在由n为根的最短路树上从1开始向N走。
接下来我们从这个最短路链上每一个点,在以1为根和以n为根的最短路树上进行bfs(仅向儿子bfs)。目的是找到对于每一个不在最短路链上的点x,找到1到x最短路与1到n最短路最后一个重合的点l[x],你也可以理解为在以1为根的最短路树上x与n的lca。同理还有以n为根的最短路上x和1的lca。
接下来我们枚举每一条不在最短路链上的边u->v,不难想到,从1到N经过边u->v的最短路一定是1->l[u]->u->v->r[v]->n的这种形式。那么在1->n的最短路上,l[u]->r[v]这一段区间内任意一条边的删除,从1到n的最短路有可能变为1->l[u]->u->v->r[v]->n。由于是最短路,所以就要更新所有这种形式的最小值。并且所有不在1->N最短路链上的边都能影响一个区间,所以这就变成了一个区间最小值问题,可以用线段树维护。用线段树维护1->N最短路上的边,线段树每一个元素代表删除这条边后最短路的长度。
最后我们扫一遍整个线段树,找最大值即可。
 

// user:halahen
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxm=500010;
int i,j,k;
int n,m,x,y,z,ans,anss=0;
int f1[maxn],f2[maxm],f3[maxm],f4[maxm],r,th;
int dis1[maxn],dis2[maxn],a[maxn],fa1[maxn],fa2[maxn];
int ll[maxn],rr[maxn];
int d1[maxm],d2[maxm],d3[maxm],d4[maxm],ha[maxn];
int read(){
	int tot=0,fh=1;
	char c=getchar();
	while ((c<'0')||(c>'9')){ if(c=='-') fh=-1; c=getchar(); }
	while ((c>='0')&&(c<='9')){ tot=tot*10+c-'0'; c=getchar(); }
	return tot*fh;
}
struct SegmentTree{
	int l[maxn*4],r[maxn*4],mi[maxn*4];
	void build(int x,int ll,int rr){
		int mid=(ll+rr)>>1;
		l[x]=ll; r[x]=rr; mi[x]=1e9;
		if (ll==rr) return;
		build(x*2,ll,mid);
		build(x*2+1,mid+1,rr);
	}
	void changge(int x,int ll,int rr,int w){
		if ((ll<=l[x])&&(r[x]<=rr)){
			mi[x]=min(mi[x],w);
			return;
		}
		if ((r[x]<ll)||(rr<l[x])) return;
		changge(x*2,ll,rr,w); changge(x*2+1,ll,rr,w);
	}
	void getans(int x,int w){
		if (l[x]==r[x]){
			if (ans<min(w,mi[x])){
				ans=min(w,mi[x]); anss=1;
			}
			else{
				if (ans==min(w,mi[x])) anss++;
			}
			return;
		}
		getans(x*2,min(w,mi[x]));
		getans(x*2+1,min(w,mi[x]));
	}
}tre;
void add(int x,int y,int w){
	r++; f2[r]=y; f3[r]=w; f4[r]=f1[x]; f1[x]=r;
}
struct node{
	int x,y,z;
	node (int xx=0,int yy=0,int zz=0){ x=xx; y=yy; z=zz; }
}d[maxn];
bool operator < (node a,node b){ return (a.x>b.x); }
void dij(int *dis,int *fa,int S){
	int i; node thi=node(0,S,0);
	priority_queue<node> mp;
	for (i=1;i<=n;i++) dis[i]=1e9;
	mp.push(thi);
	while (mp.size()!=0){
		thi=mp.top(); mp.pop();
		if (dis[thi.y]!=1e9) continue;
		i=f1[thi.y]; dis[thi.y]=thi.x; fa[thi.y]=thi.z;
		while (i!=0){
			if (dis[f2[i]]==1e9) mp.push(node(thi.x+f3[i],f2[i],i));
			i=f4[i];
		}
	}
}
void dfs(int x,int *l,int *fa){
	int i=f1[x],faa;
	ha[x]=1;
	while (i!=0){
		if (ha[f2[i]]==1){ i=f4[i]; continue; }
		if (fa[f2[i]]==0){ i=f4[i]; continue; }
		if (fa[f2[i]]%2==1) faa=f2[fa[f2[i]]+1];
		else faa=f2[fa[f2[i]]-1];
		if (faa==x){
			if (l[f2[i]]==0) l[f2[i]]=l[x];
			dfs(f2[i],l,fa);
		}
		i=f4[i];
	}
}
int main(){
	freopen("boss.in","r",stdin);
	freopen("boss.out","w",stdout);
	n=read(); m=read();
	for (i=1;i<=m;i++){
		x=read(); y=read(); z=read();
		d1[i]=x; d2[i]=y; d3[i]=z;
		add(x,y,z); add(y,x,z);
	}
	dij(dis1,fa1,1); dij(dis2,fa2,n);
	r=1; x=n;
	while (x!=1){
		if (fa1[x]%2==1){
			d4[(fa1[x]+1)/2]=1;
			x=f2[fa1[x]+1];
		}
		else{
			d4[fa1[x]/2]=1;
			x=f2[fa1[x]-1];
		}
		r++;
	}
	x=n; ll[n]=r; rr[n]=r; th=r;
	while (x!=1){
		if (fa1[x]%2==1){
			d4[(fa1[x]+1)/2]=1;
			x=f2[fa1[x]+1];
		}
		else{
			d4[fa1[x]/2]=1;
			x=f2[fa1[x]-1];
		}
		r--; ll[x]=r; rr[x]=r;
	}
	tre.build(1,1,th-1);
	memset(ha,0,sizeof(ha));
	dfs(1,ll,fa1);
	memset(ha,0,sizeof(ha));
	dfs(n,rr,fa2);
	for (i=1;i<=m;i++){
		if (d4[i]==1) continue;
		if (dis1[d1[i]]+d3[i]+dis2[d2[i]]>dis2[d1[i]]+d3[i]+dis1[d2[i]]) swap(d1[i],d2[i]);
		tre.changge(1,ll[d1[i]],rr[d2[i]]-1,dis1[d1[i]]+d3[i]+dis2[d2[i]]);
	}
	tre.getans(1,1e9);
	if (ans==dis1[n]){
		printf("%d\n",ans);
		return 0;
	}
	printf("%d\n",ans);
	return 0;
}
/*
2 4
1 2 1
1 2 1
1 2 2
1 2 3
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值