洛谷P2169 正则表达式

缩点 最短路

题目传送门

简直模板题啊!

先Tarjan缩点,再重新建边跑最短路即可。

或者把在同一点内的边权记为0,就不用重新编号建边了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 200000
#define MAXM 1000000
using namespace std;
struct edge{
    int next,to,dis;
};
int n,m,k,p,q,top;
int dfn[MAXN+5],low[MAXN+5],stack[MAXN+5],fa[MAXN+5];
int h1[MAXN+5],num[MAXN+5],h2[MAXN+5],dis[MAXN+5],que[MAXM*2+5];
edge ed[MAXM*4+5];
bool f[MAXN+5];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9'){ num=num*10+ch-48; ch=readc(); }
    return num;
}
void addedge(int x,int y,int z,int *h){
    ed[++k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k;
}
void Tarjan(int x){
    dfn[x]=low[x]=++p; stack[++top]=x; f[x]=true;
    for (int i=h1[x];i;i=ed[i].next)
        if (!dfn[ed[i].to]){
            Tarjan(ed[i].to);
            low[x]=min(low[x],low[ed[i].to]);
        }
        else if (f[ed[i].to]) low[x]=min(low[x],dfn[ed[i].to]);
    if (low[x]==dfn[x]){
        f[x]=false; num[x]=++q;
        while (stack[top+1]!=x){
            num[stack[top]]=q;
            f[stack[top--]]=false;
        }
    }
}
void spfa(int s){
    memset(dis,0x3f,sizeof(dis));
    int r=0,w=1; que[1]=s; dis[s]=0;
    while (r<w){
        int x=que[++r]; f[x]=false;
        for (int i=h2[x];i;i=ed[i].next)
            if (dis[ed[i].to]>dis[x]+ed[i].dis){
                dis[ed[i].to]=dis[x]+ed[i].dis;
                if (!f[ed[i].to]){
                    que[++w]=ed[i].to; f[ed[i].to]=true;
                }
            }
    }
}
int main(){
    n=_read(); m=_read();
    for (int i=1;i<=m;i++){
        int u=_read(),v=_read(),d=_read();
        addedge(u,v,d,h1); fa[v]=u;
    }
    for (int i=1;i<=n;i++)
        if (!dfn[i]) Tarjan(i);
    for (int i=1;i<=n;i++)
        for (int j=h1[i];j;j=ed[j].next)
            if (num[i]!=num[ed[j].to])
            addedge(num[i],num[ed[j].to],ed[j].dis,h2);
    spfa(num[1]);
    printf("%d\n",dis[num[n]]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值