[CF567E]President and Roads

题目

传送门

题解

对于 NOCAN 的情况我们其实很好判断,只需要在正向、逆向跑一边 dijkstra 得到从 \(s,t\) 到某边两点的距离,然后进行判断即可。

最难的情况其实是 YES 的情况,我们想一想这种情况出现的情景——在所有的最短路中,这条边没有可以替代的边,即没有其他相同长度的路径有和他同样的效果,称这种性质为“不可替代性”。

发现这种“不可替代性”和桥相似,即无论如何也要走过这条边,才能有最短路。

那么我们考虑把所有有可能是最短路的边建成一个新图,在图上跑 \(tarjan\) 找桥,找到的桥即为必走边,输出 YES

剩下两种情况过于简单,不作过多赘述。

话说 CF 数据是真的强,dijkstra< 打成 <= 被卡,\(SPFA\) 也被卡了 关于SPFA,它已经死了

代码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int maxn=1e5;
const int maxm=1e5;
const LL inf=1ll<<60;

struct node{int u,v,w;}es[maxm+5];
struct edge{int to,nxt,w;};
struct Points{int p;LL d;
    inline bool operator <(const Points rhs)const{
        return d>rhs.d;
    }
};

int n,m;

/** @param first 答案类型, 若为 -3 则为 NO, -2 为 CAN, -1 为 YES
 *  @param second 对于 -2 储存下一个参数
*/
pair<int,LL> ans[maxm+5];

priority_queue<Points>Q;

// queue<int>Q;
// bool inq[maxn+5];

struct Graph{
    edge e[maxm*2+5];
    int tail[maxn+5],ecnt;
    /** @brief 加一条从 @p u 到 @p v 的单向边*/
    inline void add_edge(const int u,const int v,const int w){
        e[++ecnt]=edge{v,tail[u],w};tail[u]=ecnt;
    }
    /** @brief 跑一边 dij, 结果呈现在 @p dis 中*/
    inline void dijkstra(LL* dis,const int s,const int t){
        rep(i,1,n)dis[i]=inf/*,inq[i]=0*/;
        while(!Q.empty())Q.pop();
        // dis[s]=0;Q.push(s);
        // inq[s]=1;
        // while(!Q.empty()){
        //     int u=Q.front();Q.pop();inq[u]=0;
        //     erep(i,u){
        //         if(dis[v]>dis[u]+e[i].w){
        //             dis[v]=dis[u]+e[i].w;
        //             if(!inq[v]){
        //                 Q.push(v);
        //                 inq[v]=1;
        //             }
        //         }
        //     }
        // }
        Q.push(Points{s,dis[s]=0});
        while(!Q.empty()){
            int u=Q.top().p;Q.pop();
            erep(i,u)if(dis[u]+e[i].w<dis[v])
                Q.push(Points{v,dis[v]=dis[u]+e[i].w});
        }
    }
};

/** @brief 正向图*/
Graph G;
/** @brief 逆向图*/
Graph _G;
/** @brief 以可能为最短路的边建成的图, 以用来跑 tarjan*/
Graph New;

/** @param dis[0] 从 s 开始的最短路;
 *  @param dis[1] 从 t 开始的最短路;*/
LL dis[2][maxn+5];

int s,t;

inline void Init(){
    // puts("Into Init");
    n=read(1),m=read(1),s=read(1),t=read(1);
    int u,v,w;
    rep(i,1,m){
        u=read(1),v=read(1),w=read(1);
        es[i]=node{u,v,w};
        G.add_edge(u,v,w);
        _G.add_edge(v,u,w);
    }
}

/** @brief 得到 dis 数组, 这样可以直接处理出 NO 和 CAN 的情况*/
inline void Get_dis(){
    G.dijkstra(dis[0],s,t);
    _G.dijkstra(dis[1],t,s);
    // printf("dis[0] : ");
    // rep(i,1,n)writc(dis[0][i],' ');Endl;
    // printf("dis[1] : ");
    // rep(i,1,n)writc(dis[1][i],' ');Endl;
}

LL opti_path;

int stk[maxn+5],st;bool inst[maxn+5];
int dfn[maxn+5],low[maxn+5],idx;
int bel[maxn+5],scc;

int MP[maxm+5],MP_sz;//tarjan 图上边的编号对原来边的映射

/** @brief 在 New 上找边双*/
void tarjan(const int u,const int preid){
    inst[stk[++st]=u]=true;
    dfn[u]=low[u]=++idx;
    for(int i=New.tail[u],v;i;i=New.e[i].nxt)if((i>>1)!=preid){
        v=New.e[i].to;
        if(!dfn[v])tarjan(v,i>>1),low[u]=Min(low[u],low[v]);
        else if(inst[v])low[u]=Min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        ++scc;int v;do{
            inst[v=stk[st--]]=false;
            bel[v]=scc;
        }while(v^u);
    }
}
/** @brief 在 tarjan 之后找桥*/
void dfs(const int u){
    inst[u]=true;
    for(int i=New.tail[u],v;i;i=New.e[i].nxt){
        v=New.e[i].to;
        if(bel[u]!=bel[v])ans[MP[i>>1]]=mp(-1,0ll);
        if(!inst[v])dfs(v);
    }
}

signed main(){
    Init();
    Get_dis();

    opti_path=dis[0][t];
    // if(m==99998)return 0;
    /**如果满足 dis[0][es[i].u]+es[i].w+dis[1][es[i].v]
     * 那么边 i 有可能是唯一最短路的边*/
    New.ecnt=1;//为了更快地得到边的编号
    rep(i,1,m)if(dis[0][es[i].u]+es[i].w+dis[1][es[i].v]==opti_path){
        MP[++MP_sz]=i;
        New.add_edge(es[i].u,es[i].v,0);
        New.add_edge(es[i].v,es[i].u,0);
    }
    tarjan(s,-1);
    dfs(s);
    LL delta;
    rep(i,1,m)if(ans[i].ft!=-1){
        delta=dis[0][es[i].u]+es[i].w+dis[1][es[i].v]-opti_path+1;
        if((LL)es[i].w-delta>0)ans[i]=mp(-2,delta);
        else ans[i]=mp(-3,0ll);
    }
    rep(i,1,m){
        if(ans[i].ft==-1)puts("YES");
        else if(ans[i].ft==-3)puts("NO");
        else printf("CAN %lld\n",ans[i].sd);
    }
    return 0;
}
/*
2 2 1 2
1 2 6
1 2 6
*/
posted @ 2020-08-21 16:09  Arextre  阅读(195)  评论(0编辑  收藏  举报