zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    HDU1026 Ignatius and the Princess I
    luogu_1865 A % B Problem
    luogu_1092 虫食算
    luogu_1111 修复公路
    luogu_1265 公路修建
    luogu_2330 [SCOI2005]繁忙的都市
    luogu_1613 跑路
    luogu_3386 【模板】二分图匹配
    luogu_3388 【模板】割点(割顶)
    luogu_2327 扫雷
  • 原文地址:https://www.cnblogs.com/Narh/p/9281845.html
Copyright © 2011-2022 走看看