题目大意
给定一个有向图,给定边的长度。现对于所有的边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;
}