【JZOJ5287】【NOIP2017提高组模拟】最短路

18 篇文章 0 订阅
1 篇文章 0 订阅

Description

这里写图片描述

Solution

这题就是要求一个仙人掌图上面的两点间最短路径。
那么我们一开始可以从1号节点开始跑一次spfa,然后加入两个点的在dfs树上的lca不是环上的点的话,那么直接用d[x]+d[y]-2*d[lca]就可以了。
但是如果是环上的点要怎么办?
我们环上的点可以有两条路径相互到达,那么我们可以把两个点同时跳到同一个环上然后在计算环上的距离。那么我们可以把环上的点的深度都拆成相同的,然后对于不同的环还要打上不同的标记,然后每个环上的点都要向环顶连一条边,最后构出一张新图。
构出之后再更新倍增数组,然后就按照上面的做就可以了。
环上的路径可以用从根到当前点的前缀和统计。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=2e5+7;
int i,j,k,l,t,n,m,ans,q;
int first[maxn*2],last[maxn*2],next[maxn*2],num,chang[maxn*2],o;
int first1[maxn*2],last1[maxn*2],next1[maxn*2],num1;
int dfn[maxn],d[maxn],data[maxn],dfx,c[maxn],h[maxn],f[maxn][21],g[maxn],tot,dis[maxn];
int b[maxn],fa[maxn],yi,er,deep[maxn],p[maxn];
bool bz[maxn],az,cz[maxn*2];
void add(int x,int y,int z){
    last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z;
}
void spfa(){
    memset(d,127,sizeof(d));
    int head=0,tail=1,now,i;
    bz[1]=1;d[1]=0;data[1]=1;
    while(head<tail){
        now=data[++head];
        rep(i,now){
            if(d[now]+chang[i]>=d[last[i]])continue;
            d[last[i]]=d[now]+chang[i];
            if(!bz[last[i]])bz[last[i]]=1,data[++tail]=last[i];
        }
        bz[now]=0;
    }
}
void dfs1(int x,int y,int z){
    h[++tot]=chang[z];c[x]=tot;h[tot]+=chang[p[x]];add(y,x,0),add(x,y,0);c[y]=tot;
    cz[p[x]]=cz[p[x]^1]=1;
    while(fa[x]!=y){
        x=fa[x];add(y,x,0),add(x,y,0);
        cz[p[x]]=cz[p[x]^1]=1;
        h[tot]+=chang[p[x]];
        c[x]=tot;
    }
}
void dfs(int x,int y){
    int i;dfn[x]=++dfx;
    rep(i,x){
        if((p[x]^1)==i||i>2*m+1)continue;
        if(dfn[last[i]]&&dfn[last[i]]<dfn[x]){
            cz[i]=cz[i^1]=1;
            dfs1(x,last[i],i);
            continue;
        }
        else if(!dfn[last[i]]){
            g[last[i]]=g[x]+chang[i];p[last[i]]=i;fa[last[i]]=x;
            dfs(last[i],x);
        }
    }
}
void dfs2(int x,int y){
    int i;f[x][0]=y;deep[x]=deep[y]+1;
    rep(i,x)if(last[i]!=y&&!cz[i]&&!cz[i^1])dfs2(last[i],x);
}
int lca(int &x,int &y){
    int i;if(deep[x]<deep[y])swap(x,y);
    fod(i,20,0)if(deep[f[x][i]]>deep[y])x=f[x][i];
    if(deep[x]!=deep[y])x=f[x][0];
    fod(i,20,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    if(x!=y)return f[x][0];return x;
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);num=1;
    fo(i,1,m)scanf("%d%d%d",&k,&l,&t),add(k,l,t),add(l,k,t);
    spfa();
    dfs(1,0);
    dfs2(1,0);
    fo(j,1,20)fo(i,1,n)f[i][j]=f[f[i][j-1]][j-1];
    while(q--){
        scanf("%d%d",&k,&l);yi=k,er=l;
        o=lca(k,l);
        if(q==9924){
            ans=ans;
        }
        if(c[l]&&c[k]==c[l]&&c[k]){
            ans=d[yi]+d[er]-d[k]-d[l]+min(abs(g[k]-g[l]),h[c[k]]-abs(g[k]-g[l]));
            printf("%d\n",d[yi]+d[er]-d[k]-d[l]+min(abs(g[k]-g[l]),h[c[k]]-abs(g[k]-g[l])));
        }
        else printf("%d\n",d[yi]+d[er]-2*d[o]);
        ans=d[yi]+d[er]-2*d[o];
        if(ans==246523){
            ans=ans;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值