[CF567E]President and Roads
题目
题解
对于 NO
和 CAN
的情况我们其实很好判断,只需要在正向、逆向跑一边 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
*/