TIOJ 2253:C. 街頭藝人

TIOJ 2253C. 街頭藝人


題目大意:給你一個 $n$ 城市 $m$ 邊 $k\times m$ 村莊的有向圖,城市編號 $1\sim n$,村莊編號 $n + 1\sim n + k\times m$,且第 $i$ 條邊中間的村莊為 $n + k\times(i - 1) + 1\sim n + k\times i$,城市和村莊都有一個值 $c_i$,要找到一個回路 $v_1\sim v_t, v_1 = v_t$,使得 $s_i = \sum\limits_{j = 1}^i c_{v_j}\geq 0$,並輸出此回路有多少城市和村莊可以當作出發的起點。

解法:首先找得到合法的出發點若且為若有非負環:假設有合法的出發點,那一定就是非負環,假設是非負環,我們先以隨便的出發點找出 $s_0 \sim s_t$,找到最小的 $s_{i - 1}$,那 $i$ 就可以當出發點,因為當 $j\geq i$,$s_j - s_{i - 1}\geq 0$,當 $j<i$,$s_t - s_{i - 1} + s_j\geq 0$。那如果要判斷環上的點是否能當起點,可以找到前綴和前綴最小值 $pm_i = \min(s_0\sim s_i)$、前綴和後綴最小值 $sm_i = \min(s_i\sim s_t)$,然後只要 $sm_i - s_{i - 1}\geq 0, s_t - s_{i - 1} + pm_i\geq 0$ 就可以了。

$\text{Code:}$

#pragma GCC optimize("O3")
#pragma GCC target("popcnt")
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define TIOJQQ ios::sync_with_stdio(false),cin.tie(0)
#define FOR(x,a,b) for(int x=a;x<=b;x++)
#define pb emplace_back
#define F first
#define S second

const int SIZE = 2005;
const int MSIZ = 8005;

int n, m, k;
ll w[MSIZ], dis[SIZE];
vector<int> adj[SIZE];
pair<int, int> e[MSIZ];
int a[SIZE], c[MSIZ][SIZE];

queue<int> q;
bool inq[SIZE];
int pre[SIZE], cnt[SIZE];

vector<int> st, cycle, node;
vector<ll> sum, pm, sm;

void cal (int pos) {
    fill (inq, inq + n + 1, 0);
    while (inq[pos] == 0) {
        st.pb (pre[pos]);
        inq[pos] = 1;
        pos = e[pre[pos]].F;
    }
    while (1) {
        auto [x, y] = e[st.back()];
        cycle.pb (st.back());
        st.pop_back();
        if (y == pos) break;
    }
}

void solve() {
    cin >> n >> m >> k;
    FOR (i, 1, n) cin >> a[i];
    FOR (i, 1, m) {
        auto &[x, y] = e[i];
        cin >> x >> y;
        w[i] = a[y];
        adj[x].pb (i);
        FOR (j, 1, k) {
            cin >> c[i][j];
            w[i] += c[i][j];
        }
    }
    FOR (i, 1, n) q.push (i), inq[i] = 1;
    bool f = 0;
    while (q.size()) {
        int pos = q.front();
        q.pop();
        inq[pos] = 0;
        for (int id : adj[pos]) {
            int np = e[id].S;
            if (dis[pos] + w[id] >= dis[np]) {
                dis[np] = dis[pos] + w[id];
                pre[np] = id;
                cnt[np] = cnt[pos] + 1;
                if (inq[np] == 0) {
                    inq[np] = 1;
                    q.push (np);
                }
                if (cnt[np] >= n) {
                    f = 1;
                    cal (np);
                    break;
                }
            }
        }
        if (f) break;
    }
    if (f == 0) {cout << "0\n"; return;}
    node.pb (0), sum.pb (0);
    for (int id : cycle) {
        node.pb (e[id].F);
        sum.pb (sum.back() + a[e[id].F]);
        FOR (i, 1, k) {
            node.pb (n + (id - 1) * k + i);
            sum.pb (sum.back() + c[id][i]);
        }
    }
    pm = sm = vector<ll> (sum.size());
    for (int i = 1; i < pm.size(); i++) pm[i] = min (pm[i - 1], sum[i]);
    sm.end()[-1] = sum.back();
    for (int i = sm.size() - 2; i >= 0; i--) sm[i] = min (sm[i + 1], sum[i]);
    f = 0;
    int city = 0, village = 0;
    for (int i = 1; i < sum.size(); i++) {
        if (sm[i] >= sum[i - 1] && sum.back() - sum[i - 1] + pm[i] >= 0) {
            (i % (k + 1) == 1 ? city : village)++;
            if (f == 0) {
                f = 1;
                cout << sum.size() << '\n';
                for (int j = i; j < sum.size(); j++) cout << node[j] << ' ';
                for (int j = 1; j < i; j++) cout << node[j] << ' ';
                cout << node[i] << '\n';
            }
        }
    }
    cout << city << ' ' << village << '\n';
}

int main() {
    TIOJQQ;
    solve();
}

我的分享就到這裡結束了,如果喜歡我的 $\text{Blog}$,歡迎追蹤!

留言

熱門文章