[TJOI2012] 桥 题解

传送门
弱化版

题意概要

求出在断了一条边的情况下,最短路的最大值,即最大化最小值问题。

分析

暴力:

比较好想到的思路枚举断边,然后求一次最短路,但是时间复杂度可以达到 \(O(n^3)\),无法接受
稍微优化一下就是只删除原本最短路上的边,因为如果删的不是最短路上的边,该图的最短路是不可能变的,这个优化可以拿到 0 的高分。
考虑正解,只删除最短路上的边这个思路是固定的,复杂度的瓶颈在于每次删除之后都必须求一遍最短路,我们考虑有没有办法避开最短路直接求解。

性质:

最短路上原来必须要走,在钦定最短路外一条边必须走之后不再需要被走的边必然是连续的一条路径

proof: 我们令 \(s→t\) 这条路径现在必须走,考虑最短路上原来的一条路径 \(a→b→c→d\),假设 \(a→b,c→d\) 这两段都是原来必须要走,现在不需要走的路径,而 \(b→c\) 则始终要走。如果 \(a→b\) 现在不需要走,那么必然存在一个走法绕过了 \(a→b\):从 \(1\) 走到了 \(s→t\),再经过了一些边到了 \(b\)(必须会到达 \(b\),不然无法保证 \(b→c\) 是必须走的边;同时在 \(b\) 之前必然会经过 \(s→t\),否则我们原来就有一条不需要走 \(a→b\) 的最短路径,和我们 \(a→b\) 必须要走的假设冲突了;同理,存在走法从 \(c\) 走到 \(s→t\) 再到终点 \(n\)。那么显然,我们直接从 \(t\) 到终点即可,为什么要走 \(b→c\) 呢,由此得到假设矛盾。原命题得证。

正解:

有了这个性质就可以去考虑正解了,我们枚举每条边,把边对应到我们原本的最短路径上,又由于不再被走的边必然是连续的一段区间,我们就可以用线段树实现一个区间修改,单点查询的操作。

Code:

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=1e5+10,M=2e5+10,inf=INT_MAX;
int head[N],tot;
struct node {
	int to,val,nxt;
} edge[M<<1];
void addedge(int u,int v,int w) {
	edge[++tot]= {v,w,head[u]};
	head[u]=tot;
}
int n,m,len,ans,p;
int d1[N],dn[N],vis[N],fa[2][N],used[M<<1],fir[N];
int get(int x,int *fa) {
	return x==fa[x]?x:fa[x]=get(fa[x],fa);
}
void dfs(int u,int *fa) {
	if(u==1) {
		fir[u]=++len;
		return;
	}
	dfs(fa[u],fa);
	fir[u]=++len;
}
void dij(int S,int *dis,int *fa) {
	for(int i=1; i<=n; ++i)vis[i]=0,dis[i]=inf;
	dis[S]=0;
	priority_queue<pii,vector<pii>,greater<pii>> q;
	q.push(make_pair(0,S));
	while(!q.empty()) {
		pii now=q.top();
		q.pop();
		int u=now.second,d=now.first;
		if(dis[u]<d)continue;
		for (int i=head[u]; i; i=edge[i].nxt) {
			int v=edge[i].to,w=edge[i].val;
			if(dis[u]+w<dis[v]) {
				dis[v]=dis[u]+w;
				fa[v]=u;
				q.push(make_pair(dis[v],v));
			}
		}
	}
	if(S==1)dfs(n,fa);
	for(int i=1; i<=n; i++)
		if(fir[i])fa[i]=i;
}
struct Segment_Tree {
#define ls o<<1
#define rs o<<1|1
	int tr[M<<2];
	void build(int o,int l,int r) {
		tr[o]=inf;
		if(l==r)return;
		int mid=l+r>>1;
		build(ls,l,mid),build(rs,mid+1,r);
	}
	void modify(int o,int l,int r,int L,int R,int x) {
		if(l==L&&r==R) {
			tr[o]=min(tr[o],x);
			return ;
		}
		int mid=l+r>>1;
		if(mid>=R)modify(ls,l,mid,L,R,x);
		else if(mid<L)modify(rs,mid+1,r,L,R,x);
		else modify(ls,l,mid,L,mid,x),modify(rs,mid+1,r,mid+1,R,x);
	}
	int query(int o,int l,int r,int x) {
		if(l==r)return tr[o];
		int mid=l+r>>1;
		int res=0;
		if(x<=mid)res=query(ls,l,mid,x);
		else res=query(rs,mid+1,r,x);
		return min(res,tr[o]);
	}
} T;
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1,u,v,w; i<=m; ++i) {
		scanf("%d%d%d",&u,&v,&w);
		addedge(u,v,w),addedge(v,u,w);
	}
	dij(1,d1,fa[0]);
	dij(n,dn,fa[1]);
	int u=1;
	while(u<n) {
		for(int i=head[u]; i; i=edge[i].nxt) {
			int v=edge[i].to,w=edge[i].val;
			if (dn[v]+w==dn[u]) {
				u=v,used[i]=1;
				break;
			}
		}
	}
	T.build(1,1,len);
	for(int i=1; i<=n; i++) {
		for(int j=head[i]; j; j=edge[j].nxt) {
			if(!used[j]) {
				int v=edge[j].to,w=edge[j].val;
				int l=fir[get(i,fa[0])],r=fir[get(v,fa[1])]-1;
				if(l<=r)T.modify(1,1,len,l,r,d1[i]+w+dn[v]);
			}
		}
	}
	for(int i=1; i<len; i++) {
		int tmp=T.query(1,1,len,i);
		if(ans<tmp&&tmp!=inf)ans=tmp,p=1;
		else if(ans==tmp&&tmp!=inf)p++;
	}
	if(ans==d1[n])p=m;
	printf("%d",ans);
}
posted @ 2025-07-17 16:26  黑昼白夜  阅读(92)  评论(0)    收藏  举报