CF 91E

题目大意
给定一个有向图,给定边的长度。现对于所有的边e,请判断,能否将e的长度较少到不低于1时,从s到t的最短路必定进过e,能的话输出最小调整长度,为0,输出YES,>0,输出CAN len(调整长度),不能的话输出NO

对于YES,比较直观的方法是先判断边e是否为原最短路径上的桥,这个我们可以tarjan,也可以像我一样,用拓扑排序来yy一下,我们可以确定的是,若拓扑排序到某一刻,只有点i入度为0,没被拓展过,且没有其他点入度虽然不为零,但入度被减少过(如果我们将这入度还原回去,就会发现i被包住),i就是一个割点,若它只有一条出边,此边为桥。

对于CAN和NO,就是除桥外的边,要想最短路必定过,则s到e.u的最短路程+L(调整后的边的长度)+e.v到t的最短路程比s到t的最短路大1,有解为CAN,反则为NO,这样正反两次最短路就行了,由于此题数据很多,cf上允许hack,所以spfa被卡了,要打dij才行。

贴代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100001
#define MOD 100000
using namespace std;
int n,m,s,t,top;
long long len;
int g[N],a[N+N][3],b[N],d[N],gg[N],f[N],to[N];
long long diss[N],dist[N];
bool bz[N],bz1[N];
void init(){
    static int x,y,z;
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for (int i=1;i<=m;i++){
        scanf("%d %d %d",&x,&y,&z);
        a[i][0]=y,a[i][1]=z,a[i][2]=g[x],g[x]=i;
        a[i+m][0]=x,a[i+m][1]=z,a[i+m][2]=gg[y],gg[y]=i+m;
    }
}
void Swap(int x,int y){
    swap(f[x],f[y]),to[f[x]]=x,to[f[y]]=y;
}
void up(int x,long long*dis){
    static int y;
    y=x/2;
    while (y&&dis[f[y]]>dis[f[x]])Swap(x,y),x=y,y/=2;
}
void down(int x,long long*dis){
    static int y;
    y=x+x;
    while ((y<=top&&dis[f[y]]<dis[f[x]])||(y<top&&dis[f[y+1]]<dis[f[x]])){
        if (y<top&&dis[f[y]]>dis[f[y+1]])y++;
        Swap(x,y),x=y,y+=y;
    }
}
void ins(int x,long long *dis){
    f[++top]=x,to[x]=top;
    up(top,dis);
}
void clear(long long*dis){
    Swap(1,top--);
    down(1,dis);
}
void dij(int x,long long*dis,int*g){
    static int l,r,y;
    for (int i=1;i<=n;i++)to[i]=0;
    dis[x]=1,l=0,r=1;
    ins(x,dis);
    while (top){
        y=f[1],clear(dis);
        for (int i=g[y];i;i=a[i][2])
            if (!dis[a[i][0]]||dis[a[i][0]]>dis[y]+a[i][1]){
                dis[a[i][0]]=dis[y]+a[i][1];
                if (!to[a[i][0]])
                    ins(a[i][0],dis);
                else
                    up(to[a[i][0]],dis);
            }
    }
}
int did(int x){
    return x>m?x-m:x;
}
void getlim(int*g){
    static int l,r,sum,x;
    static bool p;
    l=0,r=0,sum=0;
    for (int i=1;i<=n;i++){
        if (!b[i]&&bz[i])d[++r]=i;
        bz[i]=0;
    }
    sum=r;
    while (l<r){
        x=0;
        if (sum==1)p=1;else p=0;
        sum--;
        for (int i=g[d[++l]];i;i=a[i][2])
            if (diss[d[l]]+a[i][1]==diss[a[i][0]]&&b[a[i][0]]){
                if (x)x=-1;else x=did(i);
                if (!bz[a[i][0]])sum++,bz[a[i][0]]=1;
                if (!(--b[a[i][0]]))d[++r]=a[i][0];
            }
        if (p&&x!=-1)bz1[x]=1;
    }
}
void build(int*g){
    static int l,r;
    l=0,r=1,d[1]=t,bz[t]=1;
    while (l!=r){
        for (int i=g[d[++l]];i;i=a[i][2])
            if (diss[a[i][0]]+a[i][1]==diss[d[l]]){
                b[d[l]]++;
                if (!bz[a[i][0]])bz[d[++r]=a[i][0]]=1;
            }
    }
}
void pre(){
    dij(s,diss,g);
    dij(t,dist,gg);
    len=diss[t];
    build(gg);
    getlim(g);
}
void work(){
    static int x,y;
    static long long cost;
    len=diss[t];
    for (int i=1;i<=m;i++){
        if (bz1[i])printf("YES\n");
        else{
            y=a[i][0],x=a[i+m][0];
            if (!diss[x]||!dist[y])printf("NO\n");
            else{
                cost=diss[x]+dist[y]+a[i][1]-len;
                if (cost>=a[i][1])printf("NO\n");
                else
                    printf("CAN %I64d\n",cost);
            }
        }
    }
}
int main(){
    init();
    pre();
    work();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值