WOJ#4709 迷路
题目描述
dolls意外得到了一张藏宝图,于是他踏上了寻找宝藏的道路。在走了许多许多步,回到同一个位置以后,dolls确定自己迷路了。dolls十分生气,他觉得自己这么英明圣武的人就算迷路,也要迷路在最小的环上。于是他想知道从每个点出发最小的环有多长。藏宝图可以抽象成一个n个点m条边的,边权全为正的无向图,现在你需要求得经过每个点的最小环长是多少。
输入格式
第一行两个数n,m,表示点数和边数。下面m行每行三个整数u,v,l表示点u和点v之间有一条长度为l的无向边。
输出格式
输出n个数,表示经过每个点的最小环长,若没有则输出-1。
数据范围
n≤300,m≤40000
题解
从i点出发的最短路会构成一棵以i为根的最短路树。易知,经过i点的最小环应该是由最短路上的路径加上一条非树边构成的。由此我们可以想出这样一个算法:对于每个点i,我们构建出以它为根的最短路树,枚举非树边,如果该边之两端点之LCA为i,则用i到两点之最短路之和加上该边边权尝试更新答案。这一部分的时间复杂度应为O(nmlogn)。注意到n只有300而m只有40000,故这个算法完全可行。
注意一些细节:
1、记得开long long。
2、预先特判自环,重边只保留最小的两条。
3、关于最短路算法的选用:经计算,在该数据范围下,Floyd比跑n遍dijkstra快得多。
放上代码:
#include<bits/stdc++.h> using namespace std; #define N 310 #define M 100010 #define LL long long #define INF 0x3f3f3f3f #define pii pair<int,int> #define mp make_pair #define pb push_back int num=1,f[N]; struct node{ int u,v,w,nxt; }e[M<<1]; void add(int u,int v,int w){e[++num]=(node){u,v,w,f[u]};f[u]=num;} //build graph LL ans[N],Mp[N][N],dis[N][N]; void floyd(int n){ for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } //shortest path int inq[M<<1],Add[N]; vector<int> g[N]; void build(int u,int st){ for(int i=f[u];i;i=e[i].nxt){ int v=e[i].v,w=e[i].w; if(dis[st][v]==dis[st][u]+w&&!Add[v]){ inq[i]=inq[i^1]=1;Add[v]=1;g[u].pb(v);g[v].pb(u);build(v,st); } } } //build tree int dep[N],faz[N],sze[N],son[N],Top[N]; void dfs1(int u,int fa,int d){ dep[u]=d;sze[u]=1;faz[u]=fa; for(int i=0;i^g[u].size();i++){ int v=g[u][i]; if(v==fa) continue; dfs1(v,u,d+1);sze[u]+=sze[v]; if(!son[u]||sze[v]>sze[son[u]]) son[u]=v; } } void dfs2(int u,int st){ Top[u]=st; if(!son[u]) return; dfs2(son[u],st); for(int i=0;i^g[u].size();i++){ int v=g[u][i]; if(v==faz[u]||v==son[u]) continue; dfs2(v,v); } } int LCA(int x,int y){ int xx=Top[x],yy=Top[y]; while(xx!=yy){ if(dep[xx]<dep[yy]){swap(xx,yy);swap(x,y);} x=faz[xx];xx=Top[x]; } if(dep[x]>dep[y]) return y; return x; } //tree dissection inline int read(){ int x=0,f=1;char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-'){f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } //读优 int n,m; void write(int i){ if(ans[i]^ans[0]) printf("%lld ",ans[i]); else printf("-1 "); } int main(){ int x,y,z,sum=0; n=read();m=read(); memset(Mp,0x3f,sizeof(Mp)); memset(ans,0x3f,sizeof(ans)); memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=m;i++){ x=read();y=read();z=read(); if(i<=n) dis[i][i]=0; if(x==y){ans[x]=min(ans[x],(LL)z);continue;} if(Mp[x][y]<=z) continue; sum++;add(x,y,z),add(y,x,z);LL tmp=dis[x][y]; dis[x][y]=dis[y][x]=min(tmp,(LL)z); Mp[x][y]=Mp[y][x]=max(tmp,(LL)z); } floyd(n); for(int i=1;i<=n;i++){ memset(Add,0,sizeof(Add)); memset(dep,0,sizeof(dep)); memset(faz,0,sizeof(faz)); memset(sze,0,sizeof(sze)); memset(son,0,sizeof(son)); memset(Top,0,sizeof(Top)); memset(inq,0,sizeof(inq)); for(int j=1;j<=n;j++) g[j].clear(); build(i,i);dfs1(i,i,1);dfs2(i,i); for(int j=2;j<=num;j+=2){ int u=e[j].u,v=e[j].v,w=e[j].w;LL tmp=dis[i][u]+dis[i][v]+w; if(inq[j]||ans[i]<=tmp||LCA(u,v)^i) continue; ans[i]=tmp; } write(i); } return 0; }