3694:最短路
时间限制: 1000 ms 内存限制: 262144 KB
题目描述
给出一个 个点 条边的无向图, 个点的编号从1~ ,定义源点为1。定义最短路树如下:从源点1经过边集 到任意一点i有且仅有一条路径,且这条路径是整个图1到 的最短路径,边集 构成最短路树。 给出最短路树,求对于除了源点1外的每个点 ,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。
输入
第一行包含两个数 和 ,表示图中有 个点和 条边。
接下来 行,每行有四个数 , , , ,表示图中第 条边连接 和 权值为 , 为1表示这条边是最短路树上的边, 为0表示不是最短路树上的边。
输出
输出 个数,第i个数表示从1到 +1的要求的最短路。无法到达输出-1。
输入样例
5 9
3 1 3 1
1 4 2 1
2 1 6 0
2 3 4 0
5 2 3 0
3 2 2 1
5 3 1 1
3 5 2 0
4 5 4 0
输出样例
6 7 8 5
提示
对于100%的数据, , ,
解:
先说一说最短路树,其实题目已经解释过了。那么最短路树有什么用呢?我们可以在非树边上操作,如果单单对一条边修改,对于答案的影响可以很快求。但是似乎修改很多条边就没什么办法了?
再来说一说这道题:我们枚举非树边,那么这条边对于点的影响是什么呢?:
假如有一条红色边,那么到绿色点就有一个合法情况,走蓝色路线。
我们求出到每个点的dis。那么每在边(v,u)上的点x(除lca),经过边(v,u)的长度就是dis(v)+dis(u)+len(v,u)-dis(x)。我们很显然有一种树剖的做法,对于每个点维护合法非树边权值的最小值。
另一种做法,我们既然要求最小值,那么x一定会被最小权更新,我们对于边权排序。已经更新过的就不会被再修改了。这个可以使用并查集维护。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct lxy{
int to,next,len;
bool fg;
}eg[200005];
struct lxy1{
int u,v;
long long num;
bool operator < (const lxy1 &QAQ) const{
return num>QAQ.num;
}
}yyy;
queue <int> d;
priority_queue <lxy1> p;
int n,m,a1,a2,a3,a4,cnt;
long long dis[4005],ans[4005];
bool vis[4005];
int head[4005];
int fa[4005],tp[4005],size[4005],dep[4005],wson[4005];
int f[4005];
int findit(int x)
{
if(f[x]==x) return x;
return f[x]=findit(f[x]);
}
void add(int op,int ed,int len,int fg)
{
eg[++cnt].next=head[op];
eg[cnt].to=ed;
eg[cnt].len=len;
eg[cnt].fg=fg;
head[op]=cnt;
}
void dfs1(int u,int deeep)
{
dep[u]=deeep;
size[u]=1;
int p=0;
for(int i=head[u];i!=-1;i=eg[i].next)
if(eg[i].fg==1&&dep[eg[i].to]==0)
{
dis[eg[i].to]=dis[u]+eg[i].len;
fa[eg[i].to]=u;
dfs1(eg[i].to,deeep+1);
size[u]+=size[eg[i].to];
if(p<size[eg[i].to]){
wson[u]=eg[i].to;
p=size[eg[i].to];
}
}
}
void dfs2(int u,int las)
{
tp[u]=las;
if(wson[u]!=0) dfs2(wson[u],las);
for(int i=head[u];i!=-1;i=eg[i].next)
if(eg[i].fg==1&&tp[eg[i].to]==0)
dfs2(eg[i].to,eg[i].to);
}
int lca(int x,int y)
{
while(tp[x]!=tp[y])
{
if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
x=fa[tp[x]];
}
if(dep[x]>dep[y]) return y;
else return x;
}
int main()
{
memset(head,-1,sizeof(head));
memset(ans,-1,sizeof(ans));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a1,&a2,&a3,&a4);
add(a1,a2,a3,a4),add(a2,a1,a3,a4);
}
dfs1(1,1);dfs2(1,1);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++)
for(int j=head[i];j!=-1;j=eg[j].next)
if(eg[j].fg==0)
{
yyy.u=i;yyy.v=eg[j].to;
yyy.num=dis[i]+dis[eg[j].to]+eg[j].len;
p.push(yyy);
}
while(!p.empty()){
yyy=p.top();p.pop();
int aim=lca(yyy.u,yyy.v);
int x=findit(yyy.u);
while(dep[x]>dep[aim]){
ans[x]=yyy.num-dis[x];
f[x]=fa[x];x=fa[x];
x=findit(x);
}
x=findit(yyy.v);
while(dep[x]>dep[aim]){
ans[x]=yyy.num-dis[x];
f[x]=fa[x];x=fa[x];
x=findit(x);
}
}
for(int i=2;i<=n;i++)
printf("%lld ",ans[i]);
}