bzoj3694:最短路(最短路树+并查集)

3694:最短路

时间限制: 1000 ms 内存限制: 262144 KB

题目描述

给出一个 n 个点 m 条边的无向图, n 个点的编号从1~ n ,定义源点为1。定义最短路树如下:从源点1经过边集 T 到任意一点i有且仅有一条路径,且这条路径是整个图1到 i 的最短路径,边集 T 构成最短路树。 给出最短路树,求对于除了源点1外的每个点 i ,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。

输入

第一行包含两个数 n m ,表示图中有 n 个点和 m 条边。

接下来 m 行,每行有四个数 a i b i l i t i ,表示图中第 i 条边连接 a i b i 权值为 l i t i 为1表示这条边是最短路树上的边, t i 为0表示不是最短路树上的边。

输出

输出 n 1 个数,第i个数表示从1到 i +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%的数据, n 4000 m 100000 1 l i 100000












解:

先说一说最短路树,其实题目已经解释过了。那么最短路树有什么用呢?我们可以在非树边上操作,如果单单对一条边修改,对于答案的影响可以很快求。但是似乎修改很多条边就没什么办法了?

再来说一说这道题:我们枚举非树边,那么这条边对于点的影响是什么呢?:
这里写图片描述
假如有一条红色边,那么到绿色点就有一个合法情况,走蓝色路线。
我们求出到每个点的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]);
}

猜你喜欢

转载自blog.csdn.net/lvmaooi/article/details/80855034
今日推荐