BZOJ2125: 最短路 (圆方树)

传送门

求仙人掌上的最短路径。

把每个环中间新建一个点连向各个点,再把原来的环中的点删了。。把这个新建的点叫方点,原来的点叫圆点。方点向原点连的边为每个点到这个圆环的dfs序最小的点的最短距离。(因为 dfs序最小的点最后位于top)

注意到新图是一颗树。
从新图一个原点dfs,再维护每个方点四周的圆环信息(推荐vector)。

每次查询直接查树上路径即可。注意到如果查的lca是方点,那么最短路径要在维护的圆环信息上去取值,这个直接lowerbound即可。也可以维护一个在圆环中的位置,查询直接查最短路径。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef tr1::unordered_map<int,int> MP;
inline int rd(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}
inline void W(int x){
    static int buf[50];
    if(!x){putchar('0');return;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10;x/=10;}
    while(buf[0]){putchar(buf[buf[0]--]+'0');}
}
const int N=1e5+50;
typedef pair<int,int> pii;
int n,m,q,tot;
int ban[N];
int vis[N],ind;
pii st[N];
int pos[N],len[N],tp;
MP pos_on_cir[N];
vector<int> cir[N];
int fa[N][23],dep[N],dis[N];
struct G{
    int g[N],fm[N],nt[N],v[N],w[N],ec;
    G():ec(1){}
    inline void add(int x,int y,int c){
        nt[++ec]=g[x]; g[x]=ec; 
        v[ec]=y; fm[ec]=x;
        w[ec]=c;
    }
    inline void dfs(int x,int f,int c){
        fa[x][0]=f; dep[x]=dep[f]+1; dis[x]=dis[f]+c;
        for(int i=1;i<=20;i++)
            fa[x][i]= fa[ fa[x][i-1] ][i-1];
        for(int e=g[x];e;e=nt[e]){
            if(v[e]==f)continue;
            dfs(v[e],x,w[e]);
        }
    }
}g1,g2;
inline void dfs(int x,int f){
    vis[x]=++ind;
    for(int e=g1.g[x];e;e=g1.nt[e]){
        if((vis[g1.v[e]] && vis[g1.v[e]]>vis[x]) || g1.v[e]==f)continue;
        if(vis[g1.v[e]]){
            ++tot; cir[tot].push_back(g1.w[e]); pos_on_cir[tot][x]=0;
            for(int i=tp;i>=pos[g1.v[e]];i--){
                int sz=cir[tot].size()-1;
                pos_on_cir[tot][ st[i].first ] = sz+1;
                cir[tot].push_back(cir[tot][sz] +g1.w[st[i].second]);
            }
            int sz=cir[tot].size()-1,l=cir[tot][sz];
            int d=cir[tot][0];d=min(d,l-d);
            g2.add(tot,x,d); g2.add(x,tot,d);
            for(int i=tp;i>=pos[g1.v[e]];i--){
                d=cir[tot][ pos_on_cir[tot][st[i].first]];
                d=min(l-d,d);
                g2.add(tot,st[i].first,d);
                g2.add(st[i].first,tot,d);
            }
            ban[e]= ban[e^1] = 1;
            for(int i=tp;i>=pos[g1.v[e]];i--){
                ban[st[i].second]= (ban[st[i].second^1] = 1);
            }
        }else{
            pos[x]=++tp; st[tp]=make_pair(x,e);
            dfs(g1.v[e],x); --tp;
        }
    }
}
inline int up(int x,int d){
    for(int i=20;i>=0;i--)
        if(d&(1<<i))x=fa[x][i];
    return x;
}
inline int getlca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    x=up(x,dep[x]-dep[y]);
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        (fa[x][i]!=fa[y][i])&&(x=fa[x][i],y=fa[y][i]);
    return fa[x][0];
}
inline int query_dis(int x,int y){
    int lca=getlca(x,y);
    if(lca<=n)return dis[x]+dis[y]-2*dis[lca];
    int xc1=up(x,dep[x]-dep[lca]-1),yc1=up(y,dep[y]-dep[lca]-1);
    int ans=dis[x]-dis[xc1]+dis[y]-dis[yc1];
    int d=min(dis[xc1]-dis[lca]+dis[yc1]-dis[lca], abs(cir[lca][pos_on_cir[lca][yc1]] - cir[lca][pos_on_cir[lca][xc1]]));
    return ans+d;
}
int main(){
    n=rd(),m=rd(),q=rd(); tot=n;
    for(int i=1;i<=m;i++){
        int x=rd(),y=rd(),c=rd();
        g1.add(x,y,c); g1.add(y,x,c);
    }
    dfs(1,0);
    for(int i=2;i<=g1.ec;++i){
        if(!ban[i]){
            int u=g1.fm[i],v=g1.v[i],c=g1.w[i];
            g2.add(u,v,c);
        }
    }
    g2.dfs(1,0,0);
    while(q--)
        W(query_dis(rd(),rd())),putchar('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值