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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章