題目大意
給定一個有向圖,給定邊的長度。現對於所有的邊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;
}