您的位置:首页 > 其它

安全路径 dijkstra+LCA+并查集思想

2017-10-28 21:51 204 查看
【问题描述】

  精灵最近在农场上泛滥,他们经常会阻止牛们从农庄(牛棚1)走到别的牛棚(牛i的目的地是牛棚i)。每一个精灵只认识牛i并且知道牛i一般走到牛棚i的最短路径。所以他们在牛i到牛棚i之间的最后一条牛路上等牛i。当然,牛不愿意遇到精灵,所以准备找一条稍微不同的路径从牛棚1走到牛棚i。请你为每一头牛i找出避免精灵的最短路径长度。

  和往常一样,农场上有 n 个牛棚(编号为1..n), m 条双向牛路(编号为 1..m)把牛棚连接起来,能让所有牛到达它们的目的地。牛路 i 连接牛棚 ai 和 bi(1<=ai,bi<=N)并且需要时间 ti(1<=ti<=1000)通过。没有两条牛路连接同样的牛棚,所有牛路满足 ai!=bi。所有数据中,牛 i 使用的牛棚 1 到牛棚 i 的最短路径是唯一的。

【输入格式】

  第一行包含两个空格分开的整数:n,m。之后的 m 行,每行包含三个空格分开的数ai,bi,ti,表示一条牛路连接的 ai,bi 两个牛棚,通过这条牛路的时间为 ti。

【输出格式】

  第 1 到 n-1 行:第 i 行包含一个数,从牛棚 1 到牛棚 i+1 并且避免从牛棚 1 到牛棚 i+1 最短路径上最后一条牛路的最少的时间。如果这样的路径不存在,输出 -1。

【输入样例】

4 5

1 2 2

1 3 2

3 4 4

3 2 1

2 4 3

【输出样例】

3

3

6

【样例解释】

这个就免了吧。。。题目说的很清楚了没有什么歧义,就不需要解释了。

————————————————————————————————————————————————————————

干了真·半天。。。ORZ

首先题目表明1到所有点的最短路是唯一的,那么显然最短路网络就是一棵树(下面直接简称树了)。

题目需要我们找到从1到每个点不经过树上这个点的父边的最短路。

首先可以有这样一种想法,如果在最后的答案路径中要从一个点u走到另一个点v,那么u出发的时候一定要是最优的状态,即在树上到根结点的距离(简单来说就是在dijkstra之后直接扫描每个点i有关的点j直接用dist[j]+w(i,j)来更新ans[i])。但是对于v在u的子树中的时候这种做法显然是错误的。

有了这样的思路,我们就可以想到这样的一个结论:在树中一个点到其祖先结点的距离就是图中这两个点的最短距离。这个结论通过dijkstra的原理可以证明确实是正确的。这个结论有什么用呢?用这个结论我们可以发现最后的答案路径中只含有一条非树边。

那么思路就有了,枚举每一条非树边(u,v),设LCA(u,v)=z,那么在u->z(不包括z)上点i的答案都可以用dist[u]+dist[v]+w(u,v)-dist[i]来更新,实际上就是从1->v->u->i绕了一圈。v->z上同理。所以就已经可以想到一个迷之O(NM)的算法了ORZ

显然是会TL的,思考如何快速维护。

利用单调性,对于所有的
f2b0
非树边,按照dist[u]+dist[v]+w(u,v)从小到大排序,按照这样的顺序决策就不会出现后面的非树边的路径更新的答案会覆盖前面的非树边的路径更新的答案(对于同一个i点肯定是dist[u]+dist[v]+w(i,v)越小越好啊)。所以说对于每一条非树边,设一端为x,另一端为y,LCA(x,y)=z,那么x爬到z的过程中遇见有ans的肯定就直接跳过了。所以可以想到维护一个up数组表示某点上方第一个没有ans的点。仔细一想这个玩意儿可以用并查集的路径压缩思想维护啊有没有?!另一端y类似。

所以这个题就这么完了,关于答案的更新可以直接倍增爬山过程中实现,哪边深度大动哪边,两边点标号一样的时候就停止更新(显然会有这样的时候,仔细脑补一下这个过程)。

时间复杂度O(mlogn+n+m)(忽略了并查集的常数,并不会有什么影响)。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
#define inf 1e9+5
using namespace std;
const int maxn=100005;
const int maxm=200005;

int N,M;
struct edge{ int from,to,next,w; }E[maxm<<1];
int first[maxn],np,dist[maxn],ans[maxn],fa[maxn],dep[maxn];
struct data{ int id,v; };
struct cmp{
bool operator () (data x,data y)
{
return x.v>y.v;
}
};
struct data2{ int x,y,v; }A[maxm];
int cnt,up[maxn];

void _scanf(int &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void add_edge(int u,int v,int w)
{
E[++np]=(edge){u,v,first[u],w};
first[u]=np;
}
void data_in()
{
_scanf(N);_scanf(M);
int x,y,z;
for(int i=1;i<=M;i++)
{
_scanf(x);_scanf(y);_scanf(z);
add_edge(x,y,z);
add_edge(y,x,z);
}
}
void dijkstra()//最短路
{
for(int i=1;i<=N;i++) dist[i]=inf;
dist[1]=0;
priority_queue<data,vector<data>,cmp>pq;
pq.push((data){1,0});
data tmp;
int i,j;
while(!pq.empty())
{
tmp=pq.top(); pq.pop();
i=tmp.id;
if(dist[i]<tmp.v) continue;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to;
if(dist[i]+E[p].w<dist[j])
{
dist[j]=dist[i]+E[p].w;
fa[j]=i;
pq.push((data){j,dist[j]});
}
}
}
}
int stk[maxn],top;
void getdep(int x)
{
while(!dep[x]) stk[++top]=x,x=fa[x];
while(top)
{
x=stk[top--];
dep[x]=dep[fa[x]]+1;
}
}
void set_tree()
{
dep[1]=1;
for(int i=2;i<=N;i++) getdep(i);//递归(写作非递归)利用fa数组算每个结点的dep
}
int find(int x)//查找+路径压缩
{
while(up[x]) stk[++top]=x,x=up[x];
while(top) up[stk[top--]]=x;
return x;
}
void update(int i)//倍增爬山+跳跃+更新答案
{
while(A[i].x!=A[i].y)
{
int &now=dep[A[i].x]>dep[A[i].y]?A[i].x:A[i].y;
if(up[now]) now=find(now);
else ans[now]=A[i].v-dist[now],up[now]=fa[now],now=fa[now];
}
}
bool cmp2(data2 x,data2 y) { return x.v<y.v; }
void work()
{
dijkstra();
set_tree();
for(int i=1;i<=N;i++) ans[i]=-1;
int u,v,z;
for(int i=1;i<=M;i++)
{
u=E[i*2].from,v=E[i*2].to;
if(u==fa[v]||v==fa[u]) continue;
A[++cnt]=(data2){u,v,dist[v]+E[i*2].w+dist[u]};
}
sort(A+1,A+cnt+1,cmp2);
for(int i=1;i<=cnt;i++) update(i);
for(int i=2;i<=N;i++)
printf("%d\n",ans[i]);
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
data_in();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: