【题目泛做】迷路(最短路树)


题解:

首先解决掉重边自环。

图有可能是完全图。。。不清楚有没有卡SPFA。

考虑一个极小环,即不能通过将点减少和重排使得环长变小的环。

显然环上任意两点之间有两条环上路径,其中一条必然是最短路,否则我们用最短路可以把这个路径换掉继续减少环长。

由于数据范围十分小,我们考虑以每个点为根构造最短路树,然后枚举所有非树边,找到一个环来更新根的答案。

时间复杂度O(nmin(m,(n2))logn)O(n\min(m,{n\choose 2})\log n),就是每个点跑一次迪杰斯特拉的复杂度,注意要去掉重边。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;
typedef std::pair<ll,int> pli;
#define fi first
#define se second

inline void ckmin(ll &a,ll b){a>b?a=b:0;}
inline ll min(ll a,ll b){return a<b?a:b;}

cs int N=3e2+7;
cs ll INF=1e18;

int n,m;
ll ans[N],w[N][N];
int st[N*N],ed[N*N],ec;ll val[N*N];
struct edge{int to;ll w;};
std::vector<edge> G[N];

ll dis[N];
int pre[N],bel[N],vis[N];

std::set<pli> q;
inline void solve(cs int S){
	memset(dis+1,0x3f,sizeof(ll)*n);
	q.insert(pli(dis[S]=0,S));vis[S]=S;pre[S]=0,bel[S]=S;
	while(!q.empty()){
		int u=q.begin()->se;q.erase(q.begin());
		for(auto e:G[u])if(dis[e.to]>dis[u]+e.w){
			if(vis[e.to]==S)q.erase(pli(dis[e.to],e.to));
			q.insert(pli(dis[e.to]=dis[u]+e.w,e.to));
			pre[e.to]=u;bel[e.to]=(u==S)?e.to:bel[u];
			vis[e.to]=S;
		}
	}
	for(int re i=1;i<=ec;++i){
		int u=st[i],v=ed[i];ll w=val[i];
		if(vis[u]!=S||vis[v]!=S||pre[u]==v||pre[v]==u||bel[u]==bel[v])continue;
		ckmin(ans[S],dis[u]+dis[v]+w);
	}
	if(ans[S]>INF)ans[S]=-1;
}

signed main(){
#ifdef zxyoi
	freopen("lose.in","r",stdin);
#endif
	scanf("%d%d",&n,&m);
	memset(ans,0x3f,sizeof ans);
	memset(w,0x3f,sizeof w);
	for(int re i=1;i<=m;++i){
		int u,v;ll val;
		scanf("%d%d%lld",&u,&v,&val);
		if(u==v){ckmin(ans[u],val);}
		if(u>v)std::swap(u,v);
		if(w[u][v]<INF){
			ckmin(ans[u],val+w[u][v]);
			ckmin(ans[v],val+w[u][v]);
		}ckmin(w[u][v],val);
	}
	for(int re u=1;u<=n;++u)
	for(int re v=u+1;v<=n;++v)if(w[u][v]<INF){
		G[u].push_back((edge){v,w[u][v]});
		G[v].push_back((edge){u,w[u][v]});
		st[++ec]=u,ed[ec]=v,val[ec]=w[u][v];
	}
	for(int re i=1;i<=n;++i)solve(i),cout<<ans[i]<<" ";
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章