BZOJ2125 最短路

每个点有两个值,一个是从根到这个点的最短路d[i],一个是从根沿dfs树到这个点的距离rd[i].

之后是一个很牛逼的建图,把环上的点都连到环中深度最浅的点得到一颗树,并维护每个点所在的环以及每个环的环长。

对于一个询问(x,y),假设dep[x]>dep[y],分情况:

1.如果x和y的lca是y,那么答案为d[x]-d[y].

2.如果x和y的lca没在环里,那么答案为d[x]+d[y]-2*d[lca].

3.如果x和y的lca在环里,设x,y向上走遇到的第一个环上节点分别是a,b,那么答案为d[x]-d[a]+d[y]-d[b]+min(abs(rd[a]-rd[b]),环长-abs(rd[a]-rd[b])).

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int N=10005,M=100005;
int n,m,q,e=1,x,y,z,ti,cn,hd[N],nx[M],to[M],w[M],dl[M],dfn[N],st[N],d[N],v[N],rd[N],dp[N],bl[N],ln[N],f[N][14];
void ad(int x,int y,int z) {to[++e]=y,w[e]=z,nx[e]=hd[x],hd[x]=e;}

void spfa() {
    memset(d,0x3f,sizeof d);
    queue<int> q; q.push(1),d[1]=0;
    while(!q.empty()) {
        int u=q.front(); q.pop();
        v[u]=0;
        for(int i=hd[u];i;i=nx[i]) if(d[to[i]]>d[u]+w[i]) {
            d[to[i]]=d[u]+w[i];
            if(!v[to[i]]) v[to[i]]=1,q.push(to[i]);
        }
    }
}
void gt(int x,int y) {
    if(x==y) return;
    bl[x]=cn,ad(y,x,0),dl[st[x]]=dl[st[x]^1]=1,ln[cn]+=w[st[x]],gt(to[st[x]^1],y); 
}
void dfs(int x) {
    dfn[x]=++ti;
    for(int i=hd[x];i;i=nx[i]) if(i!=(st[x]^1)&&i<=m*2+1) {
        if(!dfn[to[i]]) rd[to[i]]=rd[x]+w[i],st[to[i]]=i,dfs(to[i]);
        else if(dfn[to[i]]<dfn[x]) ln[++cn]=w[i],gt(x,to[i]);
    }
}
void dfs2(int x) {
    for(int i=hd[x];i;i=nx[i]) if(!dl[i]&&!dp[to[i]]) f[to[i]][0]=x,dp[to[i]]=dp[x]+1,dfs2(to[i]);
}

int qry(int x,int y) {
    if(dp[x]<dp[y]) swap(x,y);
    int a=x,b=y;
    for(int i=13;~i;i--) if(dp[f[x][i]]>=dp[y]) x=f[x][i];
    if(x==y) return d[a]-d[b];
    for(int i=13;~i;i--) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    if(bl[x]&&bl[x]==bl[y]) {
        int r=abs(rd[x]-rd[y]);
        return d[a]-d[x]+d[b]-d[y]+min(r,ln[bl[x]]-r);
    }
    return d[a]+d[b]-2*d[f[x][0]];
}

int main() {
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),ad(x,y,z),ad(y,x,z);
    spfa(),dfs(1),dp[1]=1,dfs2(1);
    for(int j=1;j<14;j++)
    for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
    while(q--) scanf("%d%d",&x,&y),printf("%d\n",qry(x,y));
    return 0;
}
posted @ 2017-02-24 15:15  Monster_Yi  阅读(...)  评论(...编辑  收藏