BZOJ 3575: [Hnoi2014]道路堵塞

该博客介绍了BZOJ 3575题目——道路堵塞的解题思路。作者指出,删除一条道路后,最短路径将由原来的最短路段替换为一条非最短路径。通过SPFA算法,从边的起点开始,当到达终点的后续最短路径节点时更新答案。使用线段树维护以每个节点为终点的非最短路径总长度,并在删除某条边后,更新线段树中受影响的答案。尽管这种方法运行时间接近时限,但最终成功解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们玩一下会发现删掉一条路之后的最短路一定是原来最短路的一段且仅一段换成了一条非最短路

每一次删边,我们从边的起点出发进行spfa,当spfa到终点往后的最短路上的点时更新一下答案(的集合)

对于答案的集合呢,我们维护以每个点为终点的非最短路的总的长度(即1-p最短路+某个点->p的非最短路+p->n的最短路),这个搞个线段树就好了

然后处理完一条边之后,以这条边的终点为终点的答案全都非法了,线段树上删一删就好了

然后就TM跑了10S,卡着时限过了,业界毒瘤VFK!

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=100000+5;
const int inf=1e9;
struct Edge{int from,to,next,v;}e[N<<1];
int head[N],cnt;
void ins(int u,int v,int w){
	e[++cnt]=(Edge){u,v,head[u],w};head[u]=cnt;
}
struct Node{
	int l,r,mi;
}tr[N<<2];
#define lc o<<1
#define rc o<<1|1
void pushup(int o){
	tr[o].mi=min(tr[lc].mi,tr[rc].mi);
}
void build(int o,int l,int r){
	tr[o].l=l;tr[o].r=r;tr[o].mi=inf;
	if(l==r)return ;
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
}
void update(int o,int p,int v){
	int l=tr[o].l,r=tr[o].r;
	if(l==r)tr[o].mi=min(tr[o].mi,v);
	else{
		int mid=l+r>>1;
		if(p<=mid)update(lc,p,v);
		else update(rc,p,v);
		pushup(o);
	}
}
void modify(int o,int p){
	int l=tr[o].l,r=tr[o].r;
	if(l==r)tr[o].mi=inf;
	else{
		int mid=l+r>>1;
		if(p<=mid)modify(lc,p);
		else modify(rc,p);
		pushup(o);
	}
}
int query(int o,int mi){
	int l=tr[o].l,r=tr[o].r;
	if(l==r)return l;
	else if(mi==tr[lc].mi)return query(lc,mi);
	else return query(rc,mi);
}
int query(){
	return tr[1].mi;
}
int dis[N],all;
bool inq[N];
void spfa(int s){
	memset(dis,0x3f,sizeof(dis));
	queue<int>q;q.push(s);dis[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();inq[u]=0;
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].v){
				dis[v]=dis[u]+e[i].v;
				if(!inq[v]){
					inq[v]=true;
					q.push(v);
				}
			}
		}
	}
}
bool del[N<<1],onpath[N];
int d[N],pos[N];
void find(int s,int lim){
	d[s]=dis[s];queue<int>q;q.push(s);
	while(!q.empty()){
		int u=q.front();q.pop();inq[u]=0;
		for(int i=head[u];i;i=e[i].next){
			if(del[i])continue;
			int v=e[i].to;
			if(pos[v]>=lim)update(1,pos[v],d[u]+e[i].v+all-dis[v]);
			else if(d[v]>d[u]+e[i].v){
				d[v]=d[u]+e[i].v;
				if(!inq[v]){
					inq[v]=true;
					q.push(v);
				}
			}
		}
	}
}
int L[N<<1];
int main(){
	//freopen("a.in","r",stdin);
	int n,m,l;scanf("%d%d%d",&n,&m,&l);
	for(int i=1;i<=m;i++){
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		ins(u,v,w);
	}
	for(int i=1;i<=l;i++){
		scanf("%d",&L[i]);
		onpath[e[L[i]].from]=true;
		onpath[e[L[i]].to]=true;
		pos[e[L[i]].to]=i;
	}
	build(1,1,l);
	spfa(1);
	memset(d,0x3f,sizeof(d));d[1]=0;
	all=dis[n];
	for(int i=1;i<=l;i++){
		del[L[i]]=true;
		find(e[L[i]].from,i);
		if(query()>=inf)puts("-1");
		else printf("%d\n",query());
		del[L[i]]=false;
		modify(1,i);
	}
	return 0;
}</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值