【題目泛做】迷路(最短路樹)


題解:

首先解決掉重邊自環。

圖有可能是完全圖。。。不清楚有沒有卡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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章