BZOJ1576: [Usaco2009 Jan]安全路经Travel

10 篇文章 0 订阅
7 篇文章 0 订阅

题目大意:求从1到每个点不经过最短路的最后一条边的所有方案中的最短路


首先构出最短路径树(有所有在最短路上的边构成的树)

这样所有的答案路径都是从起点开始沿着树上的边走到一个点然后走一个不是树上的边,然后再在树上走

这样对于一条非树边(u,v)他能更新的答案就是对于所有的在(u,v)树上路径上的点(不包括LCA)x,ans[x]=min(ans[x],dis[u]+dis[v]+w-dis[x])

显然暴力更新是不可取的,所以我们可以把非树边按照dis[u]+dis[v]+w排序,这样第一个更新到的就是答案,接着可以用并查集保证每个点只被更新一次

卡SPFA差评!!!


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 200010
using namespace std;
int A[N],B[N],C[N];
int to[N<<1],nxt[N<<1],num[N<<1],w[N<<1],pre[N],cnt;
void ae(int ff,int tt,int ww,int nn)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	w[cnt]=ww;
	num[cnt]=nn;
	pre[ff]=cnt;
}
void ae(int ff,int tt)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
}
int d[N],bef[N];
bool vis[N];
struct qqq{int x,w;};
bool operator <(const qqq &x,const qqq &y){return x.w>y.w;}
priority_queue<qqq>q;
void dij()
{
	memset(d,0x3f,sizeof(d));
	d[1]=0;
	q.push((qqq){1,0});
	int i,j,x;
	while(!q.empty())
	{
		x=q.top().x;q.pop();
		if(vis[x]) continue;
		vis[x]=true;
		for(i=pre[x];i;i=nxt[i])
		{
			j=to[i];
			if(vis[j]||d[j]<=d[x]+w[i]) continue;
			bef[j]=num[i];
			d[j]=d[x]+w[i];
			q.push((qqq){j,d[j]});
		}
	}
}
bool spc[N];
struct ppp{int u,v,w;}b[N];
bool cmp(ppp x,ppp y){return x.w<y.w;}
int FA[N];
void build(int x)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==FA[x]) continue;
		FA[j]=x;
		build(j);
	}
}
int fa[N];
int find(int x)
{
	if(fa[x]==x) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
int ans[N];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int i,j,x,y,z;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&A[i],&B[i],&C[i]);
		ae(A[i],B[i],C[i],i);ae(B[i],A[i],C[i],i);
	}
	dij();
	cnt=0;memset(pre,0,sizeof(pre));
	for(i=2;i<=n;i++)
	{
		x=bef[i];
		ae(A[x],B[x]);
		ae(B[x],A[x]);
		spc[x]=true;
	}
	build(1);
	for(i=1;i<=n;i++)
	fa[i]=i;
	int cn=0;
	for(i=1;i<=m;i++)
	if(!spc[i])
	{
		cn++;
		b[cn]=(ppp){A[i],B[i],d[A[i]]+d[B[i]]+C[i]};
	}
	sort(b+1,b+cn+1,cmp);
	for(i=1;i<=cn;i++)
	{
		x=find(b[i].u);y=find(b[i].v);
		while(x!=y)
		{
			if(d[x]<d[y]) swap(x,y);
			ans[x]=b[i].w-d[x];
			fa[x]=FA[x];
			x=find(x);
		}
	}
	for(i=2;i<=n;i++)
	{
		if(ans[i]) printf("%d\n",ans[i]);
		else puts("-1");
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值