bzoj 2125 最短路——仙人掌两点间最短路

Posted narh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2125 最短路——仙人掌两点间最短路相关的知识,希望对你有一定的参考价值。

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2125

因为看了TJ又抄了标程,现在感觉还是轻飘飘的……必须再做一遍。

两点间的情况:

1.直到 lca 都没有在一个环上的部分;

2.本来就处在一个环上;

3.本来不在一个环上,快到 lca 的时候开始处在一个环上了。

第一种情况就普通弄就行。处理倍增 lca 数组和根到每个点的最短路dis值。

第二种情况在环上两部分取较短的就行。

第三种情况是前两种的结合。需要找到 p 和 q 刚开始在同一个环上时的那两个进入点 x 和 y。然后dis[ p ] - dis[ x ] + dis[ q ] - dis[ y ]再加上 p、q 环上两部分较短的。

怎么取较短的呢?

可以弄dfs序的距离。

实现的时候第二种和第三种情况可以合并。

细节有一些不明白:环的最高点的深度和环上别的点不一样,也行吗?还有边的数组大小怎么算?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e4+5;
int n,m,q,hd[N],xnt=1,g[N],len[N],cnt,dep[N],f[N][20];
int dis[N],ds[N],fa[N],dfn[N],tim;
bool vis[N];
struct Ed{
  int nxt,to,w;bool del;
  Ed(int n=0,int t=0,int w=0):nxt(n),to(t),w(w) {del=0;}
}ed[N<<3];
int tabs(int k){return k<0?-k:k;}
void add(int x,int y,int z)
{
  ed[++xnt]=Ed(hd[x],y,z);hd[x]=xnt;
}
void spfa()
{
  memset(dis,0x3f,sizeof dis);dis[1]=0;
  queue<int> q;q.push(1);vis[1]=1;
  while(q.size())
    {
      int k=q.front();q.pop();vis[k]=0;//don‘t forget visk=0!!
      for(int i=hd[k],v;i;i=ed[i].nxt)
    if(dis[v=ed[i].to]>dis[k]+ed[i].w)
      {
        dis[v]=dis[k]+ed[i].w;
        if(!vis[v])vis[v]=1,q.push(v);
      }
    }
}
void circle(int y,int e)
{
  int x=ed[e].to;//!
  len[++cnt]=ed[e].w;g[y]=cnt;
  ed[e].del=ed[e^1].del=1;//don‘t forget del!
  add(x,y,0);add(y,x,0);// edw has no limit--only for bfs the dep
                          // edw is guaranteed by ds[]
  for(e=fa[y];(y=ed[e^1].to)!=x;e=fa[y])
    {
      len[cnt]+=ed[e].w;g[y]=cnt;ed[e].del=ed[e^1].del=1;
      add(x,y,0);add(y,x,0);
    }
  len[cnt]+=ed[e].w;g[x]=cnt;
}
void dfs(int cr)
{
  dfn[cr]=++tim;
  for(int i=hd[cr],v;i;i=ed[i].nxt)
    if(!dfn[v=ed[i].to])
      {
    fa[v]=i;ds[v]=ds[cr]+ed[i].w;dfs(v);
      }
    else if(i!=(fa[cr]^1)&&dfn[v]<dfn[cr])circle(cr,i);//cr!!!
}
void bfs()
{
  queue<int> q;q.push(1);dep[1]=1;
  while(q.size())
    {
      int k=q.front();q.pop();
      for(int i=hd[k],v;i;i=ed[i].nxt)
    if(!ed[i].del&&!dep[v=ed[i].to])
      {
        dep[v]=dep[k]+1;q.push(v);
        f[v][0]=k;
        for(int j=1;j<=15;j++)//j not i
          {
        f[v][j]=f[f[v][j-1]][j-1];
          }
      }
    }
}
int lca(int x,int y)
{
  if(dep[x]<dep[y])swap(x,y);
  int p=x,q=y;
  for(int i=15;i>=0;i--)
    if(dep[f[x][i]]>=dep[y])x=f[x][i];
  if(x==y)return dis[p]-dis[q];
  for(int i=15;i>=0;i--)
    if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
  if(g[x]!=g[y]||(!g[x]&&!g[y]))return dis[p]+dis[q]-2*dis[f[x][0]];//&& !g[x]&&!g[y]!!!
  int k=tabs(ds[x]-ds[y]);
  return dis[p]-dis[x]+dis[q]-dis[y]+min(k,len[g[x]]-k);
  // include p&q in different circle but x&y in the same circle;
  //because the dep of nodes in the same circle are almost the same
}
int main()
{
  scanf("%d%d%d",&n,&m,&q);int x,y,z;
  for(int i=1;i<=m;i++)
    {
      scanf("%d%d%d",&x,&y,&z);
      add(x,y,z);add(y,x,z);
    }
  spfa();
  dfs(1);
  bfs();
  for(int i=1;i<=q;i++)
    {
      scanf("%d%d",&x,&y);
      printf("%d
",lca(x,y));
    }
  return 0;
}

 

以上是关于bzoj 2125 最短路——仙人掌两点间最短路的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2125 最短路——仙人掌两点间距离

BZOJ.2125.最短路(仙人掌 最短路Dijkstra)

bzoj 2125 最短路 点双 写错了自闭

题解Bzoj2125最短路

BZOJ2125: 最短路

BZOJ 2125 最短路