[BZOJ1880][Sdoi2009]Elaxia的路线(最短路+拓扑序)

=== ===

这里放传送门

=== ===

题解

首先我们可以知道如果把所有最短路上的边挑出来,再按照最短路中dis数组的递推关系给它加上方向的话这就是一个有向无环图。而如果把所有两个最短路的公共边挑出来的话,这个有向无环图中的最长链就是答案。并且判断一条边在最短路上的方式就是 dis[su]+w(u,v)+dis[ut]=dis[st] 。并且因为这是无向图,所以求任意一个点到终点的距离只需要以终点为起点再跑一遍最短路就可以了。需要注意的问题就是公共路径从不同的方向经过也是可以的,所以需要把一对起点和终点倒过来再做一遍。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define clear(x)(memset(x,127,sizeof(x)))
#define inc(x)(x=(x%5000)+1)
using namespace std;
int n,m,p[2000],a[1000010],next[1000010],d1[2000],d2[2000],d3[2000],d4[2000],w[1000010];
int x1,y1,x2,y2,tot,cnt,point[2000],ans,L[2000],*f,*g,*F,*G;
struct edge{
    int to,w,nxt;
}e[600010];
void add(int x,int y,int v){
    tot++;a[tot]=y;w[tot]=v;next[tot]=p[x];p[x]=tot;
}
void addedge(int x,int y,int v){
    cnt++;e[cnt].to=y;e[cnt].w=v;e[cnt].nxt=point[x];point[x]=cnt;
}
void SPFA(int s,int *d){
    int q[5000],head,tail;
    bool ext[3000];
    memset(ext,false,sizeof(ext));
    head=0;tail=1;q[tail]=s;d[s]=0;ext[s]=true;
    while (head!=tail){
        int u;inc(head);u=q[head];ext[u]=false;
        for (int i=p[u];i!=0;i=next[i])
          if (d[a[i]]>d[u]+w[i]){
              d[a[i]]=d[u]+w[i];
              if (ext[a[i]]==false){
                  inc(tail);q[tail]=a[i];ext[a[i]]=true;
              }
          }
    }
}
bool init(int i,int num,int *ds,int *dt,int len){
    return (ds[i]+dt[a[num]]+w[num]==len);
}
void find_long(int s1,int t1,int s2,int t2){
    int q[5000],head,tail,in[3000];
    memset(in,0,sizeof(in));
    memset(point,0,sizeof(point));
    memset(L,0,sizeof(L));
    for (int i=1;i<=n;i++)
      for (int j=p[i];j!=0;j=next[j])
        if (init(i,j,f,g,f[t1])&&init(i,j,F,G,F[t2])){
            addedge(i,a[j],w[j]);++in[a[j]];//加入有向边
        } 
    head=tail=0;
    for (int i=1;i<=n;i++)
      if (in[i]==0) q[++tail]=i;
    while (head!=tail){
        int u;inc(head);u=q[head];
        for (int i=point[u];i!=0;i=e[i].nxt){
            int v=e[i].to;
            L[v]=max(L[v],L[u]+e[i].w);
            ans=max(ans,L[v]);//求最长链的同时更新答案
            --in[v];
            if (in[v]==0){inc(tail);q[tail]=v;}
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    for (int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    clear(d1);clear(d2);clear(d3);clear(d4);
    f=d1;g=d2;F=d3;G=d4;
    SPFA(x1,f);SPFA(y1,g);SPFA(x2,F);SPFA(y2,G);
    find_long(x1,y1,x2,y2);
    swap(F,G);//交换起点和终点(x2,y2),交换最短路数组的指针
    find_long(x1,y1,y2,x2);
    printf("%d\n",ans);
    return 0;
}

偏偏在最后出现的补充说明

通过最短路构造出的图啊树啊总是可以来做一些奇怪的事情?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值