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=,f[N];
struct node{
int u,v,w,nxt;
}e[M<<];
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=;k<=n;k++)
for(int i=;i<=n;i++)
for(int j=;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
//shortest path
int inq[M<<],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^]=;Add[v]=;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]=;faz[u]=fa;
for(int i=;i^g[u].size();i++){
int v=g[u][i];
if(v==fa) continue;
dfs1(v,u,d+);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=;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=,f=;char ch;
for(ch=getchar();(ch<''||ch>'')&&ch!='-';ch=getchar());
if(ch=='-'){f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=(x<<)+(x<<)+ch-'';ch=getchar();}
return x*f;
}
//读优
int n,m;
void write(int i){
if(ans[i]^ans[]) printf("%lld ",ans[i]);
else printf("-1 ");
}
int main(){
int x,y,z,sum=;
n=read();m=read();
memset(Mp,0x3f,sizeof(Mp));
memset(ans,0x3f,sizeof(ans));
memset(dis,0x3f,sizeof(dis));
for(int i=;i<=m;i++){
x=read();y=read();z=read();
if(i<=n) dis[i][i]=;
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=;i<=n;i++){
memset(Add,,sizeof(Add));
memset(dep,,sizeof(dep));
memset(faz,,sizeof(faz));
memset(sze,,sizeof(sze));
memset(son,,sizeof(son));
memset(Top,,sizeof(Top));
memset(inq,,sizeof(inq));
for(int j=;j<=n;j++) g[j].clear();
build(i,i);dfs1(i,i,);dfs2(i,i);
for(int j=;j<=num;j+=){
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 ;
}

  

最新文章

  1. 微信小程序 教程之引用
  2. UITableview中怎么找到每个cell
  3. Vue 过滤器与计算属性
  4. [转] 基于PHP Stream Wrapper开发有趣应用场景
  5. codevs 3732 解方程
  6. 【大坑】DataGridView多线程更新修改Cell单元格卡死
  7. HDU4283:You Are the One(区间DP)
  8. 液晶顯示器 LCD (Liquid Crystal Disply )
  9. IISExpress配置文件
  10. 【转】sublime text 3 显示空格和Tab
  11. .NETCore 快速开发做一个简易商城
  12. Linux常用的基础命令总结
  13. css详细笔记
  14. gitlab 之 项目管理
  15. JAVA 变量 数据类型 运算符 知识小结
  16. 搭建一个简单的node.js服务器
  17. WPF自定义路由事件(一)
  18. sitecore系统教程之部署架构方式分析
  19. 获取IP及判断IP是否在区间
  20. YII插件

热门文章

  1. django快速搭建blog
  2. android读取xml
  3. UE4从4.15移植到4.16
  4. maven入门问题解决
  5. 使用Git上传本地项目到http://git.oschina.net
  6. oracle 如何更改密码的hash
  7. Git remotes/origin/pr/* 分支清理,代码回退等
  8. fedora23禁用不需要的服务?--systemd服务单元?
  9. python 网络编程 代码版
  10. 在sql中使用函数,遇到net.sf.jsqlparser.parser.ParseException异常