伊吹萃香 題解

_zqh發表於2024-10-07

題意

(很複雜,真的不想概括,以下是原題面)

在幻想鄉,伊吹萃香是能夠控制物體密度的鬼王。因為能夠控制密度,所以萃香能夠製造白洞和黑洞,並可以隨時改變它們。某一天萃香閒著無聊,在妖怪之山上設定了一些白洞或黑洞,由於引力的影響,給妖怪們帶來了很大的麻煩。於是他們決定找出一條消耗體力最少的路,來方便進出。已知妖怪之山上有 \(N\) 個路口(編號 \(1\)..\(N\) ),每個路口都被萃香設定了一定質量白洞或者黑洞。原本在各個路口之間有M條單向路,走過每一條路需要消耗一定量的體力以及 \(1\) 個單位的時間。由於白洞和黑洞的存在,走過每條路需要消耗的體力也就產生了變化,假設一條道路兩端路口黑白洞的質量差為 \(delta\)

  1. 從有白洞的路口走向有黑洞的路口,消耗的體力值減少 \(delta\),若該條路徑消耗的體力值變為負數的話,取為 \(0\)

  2. 從有黑洞的路口走向有白洞的路口,消耗的體力值增加 \(delta\)

  3. 如果路口兩端均為白洞或黑洞,消耗的體力值無變化。

由於光是放置黑洞白洞不足以體現萃香的強大,所以她決定每過 \(1\) 個單位時間,就把所有路口的白洞改成黑洞,黑洞改成白洞。當然在走的過程中你可以選擇在一個路口上停留 \(1\) 個單位的時間,如果當前路口為白洞,則不消耗體力,否則消耗 \(s_i\) 的體力。現在請你計算從路口 \(1\) 走到路口 \(N\) 最小的體力消耗。保證一定存在道路從路口 \(1\) 到路口 \(N\)

題解

最短路,維護一個 \(f(i, h)\) 表示到達 \(i\) 點且到達之後的洞為 \(h\)

先考慮停留的情況:

若該洞為白洞:

\[f(u, 1 - h) = f(u, h) \]

為黑洞:

\[f(u, 1 - h) = f(u, h) + s_u \]

(判黑白洞就用時間加最開始的形態模 \(2\)\(1 - h\) 就是把原來的洞反過來)

如果不停留:

\[f(v, h ^ {\prime}) = \min\{f(v, h ^ {\prime}), f(u, h) + w\} \]

\(w\) 為當前消耗代價,\(h ^ {\prime}\) 為到達 \(v\) 之後的洞型)

\(w\) 可以開個函式算一下。

順便提一嘴,如果用堆跑最短路會被卡(也有可能是我常數太大了),建議改成佇列(反正也能過)。

namespace zqh {
const int N = 5005;

struct node { // 結點狀態
    int hol;
    int wei;
    int sta;
} h[N];
struct point { // 最短路的狀態
    int u, t, k;
};
int n, m, dis[N][2]; // 上文 f 陣列
vector<pii> g[N]; // 存圖

int calc(int weiu, int weiv, int holu, int holv, int w) { // 計算代價,w 為最開始的代價
    if (holu == holv) {
        return w;
    }
    if (holu == 1 && holv == 0) {
        return w + abs(weiu - weiv);
    }
    if (holu == 0 && holv == 1) {
        return max(w - abs(weiu - weiv), 0LL);
    }
}

void dijkstra() { // 貌似不是 dij
    memset(dis, 0x3f, sizeof dis); // 賦初值
    queue<point> q;
    q.push({1, 0, 0});
    dis[1][h[1].hol] = 0;
    while (q.size()) {
        point t = q.front();
        //			cout << t.u << " " << t.t << " " << t.k << endl;
        q.pop();
        int u = t.u, hole_u = ((h[u].hol + t.t) & 1); // hole_u 為當前的洞狀態
        if (hole_u == 0) { // 停留,判黑白洞,賽時沒盤,成功掛分
            if (dis[u][1 - hole_u] > dis[u][hole_u]) {
                dis[u][1 - hole_u] = dis[u][hole_u];
                q.push({u, t.t + 1, dis[u][1 - hole_u]});
            }
        } else {
            if (dis[u][1 - hole_u] > dis[u][hole_u] + h[u].sta) {
                dis[u][1 - hole_u] = dis[u][hole_u] + h[u].sta;
                q.push({u, t.t + 1, dis[u][1 - hole_u]});
            }
        }
        for (int i = 0; i < g[u].size(); i++) {
            int v = g[u][i].first, w = g[u][i].second;
            int hole_v = ((h[v].hol + t.t + 1) & 1); // hole_v 同理
            int val = calc(h[u].wei, h[v].wei, hole_u, 1 - hole_v, w); // 計算代價
            if (dis[v][hole_v] > dis[u][hole_u] + val) { // 轉移
                dis[v][hole_v] = dis[u][hole_u] + val;
                q.push({v, t.t + 1, dis[v][hole_v]});
            }
        }
    }
}

void init() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> h[i].hol;
    }
    for (int i = 1; i <= n; i++) {
        cin >> h[i].wei;
    }
    for (int i = 1; i <= n; i++) {
        cin >> h[i].sta;
    }
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({v, w});
    }
}

void solve() {
    dijkstra();
    cout << min(dis[n][0], dis[n][1]); // 最後可以是黑白兩種
}

void main() {
    init();
    solve();
}
}  // namespace zqh
/*
4 5
1 0 1 0
10 10 100 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200

130
*/

\[\]

相關文章