1. 程式人生 > 實用技巧 >【Codeforces Round #669 (Div. 2) E】Egor in the Republic of Dagestan

【Codeforces Round #669 (Div. 2) E】Egor in the Republic of Dagestan

題目連結

點我呀

翻譯

你是小 \(A\) 的管家,小 \(A\) 要從點 \(1\) 到點 \(n\),點與點之間的邊(有向邊)有黑色(0)和白色(1)兩種, 你可以給每個點塗色 (黑色/白色)。

黑色的點,只能沿著黑色的邊接著走,白色的點同理,即如果 \(x\) 是黑色的,那麼你接下來只能沿著邊 \((x,y)\) 走,這條邊 \((x,y)\) 也得是黑色的。

每個點的顏色由你決定,你不想讓小 \(A\) 太快到達點 \(n\) (到達點 \(n\) 的最短路是所有塗色方案中最長的),或者最好就不讓他到達點 \(A\) (畸形的愛)。

讓你輸出塗色方案,以及該方案下到達點 \(n\) 的最短路。

題解

就直接說做法了。

首先將所有的邊方向,轉換成從點 \(n\) 到點 \(1\) 的問題。

這樣處理,就能保證到達點 \(x\) 的話,點 \(x\) 是什麼顏色的,則走到 \(x\) 這個點經過的邊也對應的是什麼顏色。

每個點有兩種染色的可能, 黑/白。

那麼我們就設 \(b[x]\) 表示到達點 \(x\)\(x\) 染成了黑色(\(black\)) 的最短路(這裡的最短路就是最壞情況下的最短路, 所以等下你會發現這個 \(b[x]\) 的值不是由某個
\(b[prex]\) 轉移來的,而是經過另外一個 \(dp[prex]\) 轉移過來的), \(w[x]\) 表示到達點 \(x\)

\(x\) 染成了白色的最短路。

此外還要設一個 \(dp[x]\) 表示到達點 \(x\) 最壞情況需要的最短路, \(b[x]\)\(w[x]\) 的值就是由這個 \(dp\) 的值轉移得到的。

最後,再定義一個數組 \(col[N]\) 來記錄每個節點最後的顏色。

這個 \(col\) 陣列顯然可以根據 \(b[x]\)\(c[x]\) 的大小得到,哪一個大,就染成對應的顏色即可 (一樣大則任意塗, 因為到了這個點以後, 後面的路徑就和當前點的顏色無關了, 這也是圖反向之後的好處)。

\(b\) 陣列和 \(w\) 陣列的生成過程和求最短路問題類似。

最開始的時候,令 b[n]=w[n]=dp[n]=0

, 其他的 \(b,w,dp\) 的值都設為無窮大。

對於所有染色完畢的點 (這裡如果 某個點 \(x\)\(max(b[x],w[x])\) 的值是無窮大, 即 \(b[x]\)\(w[x]\) 的值中
有一個還沒算出來, 就認為該點還沒染色完畢, 否則像初始的點 \(n\), \(b\)\(w\) 的最大值都是 \(0\), 則認為它染色完畢了, 也即這個點染成黑色
或白色的最短路都算出來了), 選擇其中 \(dp\) 值最小的點 \(x\), 對於所有的出邊 \((x,y)\), 如果邊是黑邊, 那麼 b[y] = min(b[y],dp[x]+1)。如果是白邊,則執行 w[y] = min(w[y],dp[x]+1)

\(b[y]\)\(w[y]\) 的值都算出來以後,令 dp[y] = max(b[y],w[y]) ,也即 \(y\) 有兩種染色方案,選擇從 \(1\)\(y\) 的最短路最長的那一種染色方案。

(這個時候 \(y\) 的顏色也就確認了), 那麼將 \(y\) 加入佇列中, 供下一次取出進行節點的更新。

這裡對 \(col\) 陣列的賦值可以等到 \(bfs\) 做完之後再一遍 \(for\) 迴圈得到(根據 \(b[i]\)\(w[i]\) 的比較結果獲得)。

最後輸出 \(dp[1]\) 的值就可以了 (如果 \(dp[1]\) 的值為無窮大, 說明有染色方案可以讓小 \(A\) 無法從 \(1\)\(n\)),那麼直接輸出 \(-1\) 即可。

感性理解一下,就是求最短路,然後某個點的染色方案,根據到這點為黑色和白色的最短路的大小判斷,誰大就染成對應顏色。這樣的染色方案就能使得到達每個點的最短路是最長的了。

程式碼

#include <bits/stdc++.h>
using namespace std;

const int N = 5e5;

int n, m;
int b[N + 10], w[N + 10], dp[N + 10], col[N + 10];
vector<int> bg[N + 10], wg[N + 10];
queue<int> dl;

int main(){
    #ifdef LOCAL_DEFINE
        freopen("E://9.AlgorithmCompetition//Visitor.txt","r",stdin);
    #endif
    ios::sync_with_stdio(0),cin.tie(0);
    cin >> n >> m;
    for (int i = 1;i <= m; i++){
        int u, v, t;
        cin >> u >> v >> t;
        if (t == 0){
            bg[v].push_back(u);
        }else{
            wg[v].push_back(u);
        }
    }
    for (int i = 1;i <= n; i++){
        b[i] = w[i] = dp[i] = n;
    }
    b[n] = w[n] = dp[n] = 0;
    dl.push(n);
    while (!dl.empty()){
        int x = dl.front();dl.pop();
        int len = bg[x].size();
        for (int i = 0;i < len; i++){
            int y = bg[x][i];
            if (b[y] > dp[x] + 1){
                b[y] = dp[x] + 1;
                if (max(b[y],w[y])<n){
                    dp[y] = max(b[y],w[y]);
                    dl.push(y);
                }
            }
        }
        len = wg[x].size();
        for (int i = 0;i < len; i++){
            int y = wg[x][i];
            if (w[y] > dp[x] + 1){
                w[y] = dp[x] + 1;
                if (max(b[y],w[y])<n){
                    dp[y] = max(b[y],w[y]);
                    dl.push(y);
                }
            }
        }
    }
    if (dp[1] == n){
        cout << -1 << endl;
    }else{
        cout << dp[1] << endl;
    }
    for (int i = 1;i <= n; i++){
        if (b[i] < w[i]){
            cout << 1;
        }else{
            cout << 0;
        }
    }
    return 0;
}