Submission #978215

#TimeUsernameProblemLanguageResultExecution timeMemory
978215model_codeBoard Game (JOI24_boardgame)C++17
100 / 100
529 ms52772 KiB
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

template <class T>
using vc = vector<T>;

#define FOR1(a) for (ll _ = 0; _ < ll(a); ++_)
#define FOR2(i, a) for (ll i = 0; i < ll(a); ++i)
#define FOR3(i, a, b) for (ll i = a; i < ll(b); ++i)
#define overload3(a, b, c, d, ...) d
#define FOR(...) overload3(__VA_ARGS__, FOR3, FOR2, FOR1)(__VA_ARGS__)

#define all(x) x.begin(), x.end()
#define len(x) ll(x.size())
#define elif else if

template <typename T>
T POP(deque<T> &que) {
  T a = que.front();
  que.pop_front();
  return a;
}

template <class T, class S>
inline bool chmax(T &a, const S &b) {
  return (a < b ? a = b, 1 : 0);
}
template <class T, class S>
inline bool chmin(T &a, const S &b) {
  return (a > b ? a = b, 1 : 0);
}

// stable sort
template <typename T>
vector<int> argsort(const vector<T> &A) {
  vector<int> ids(len(A));
  iota(all(ids), 0);
  sort(all(ids),
       [&](int i, int j) { return (A[i] == A[j] ? i < j : A[i] < A[j]); });
  return ids;
}

// A[I[0]], A[I[1]], ...
template <typename T>
vc<T> rearrange(const vc<T> &A, const vc<int> &I) {
  vc<T> B(len(I));
  FOR(i, len(I)) B[i] = A[I[i]];
  return B;
}

constexpr ll INF = 1'000'000'000'000'000'000;

// 点集合 V からの距離
// 距離が x1 または x2 であり, f(u,v) で与える.
template <typename F>
vc<ll> bfs(int N, vc<vc<int>> &G, vc<int> &V, F f) {
  assert(!V.empty());
  vc<ll> dist(N, INF);
  deque<int> que;
  for (auto &v: V) { dist[v] = 0, que.push_back(v); }
  while (len(que)) {
    int v = POP(que);
    for (int w: G[v]) {
      ll x = f(v, w);
      assert(x == 0 || x == 1);
      if (chmin(dist[w], dist[v] + x)) {
        if (x == 0)
          que.push_front(w);
        else
          que.push_back(w);
      }
    }
  }
  return dist;
}

void out(vc<ll> ANS) {
  for (ll x: ANS) cout << x << "\n";
}

void solve() {
  // 入力を読み込む
  int N, M, K;
  cin >> N >> M >> K;
  vc<vc<int>> G(N);
  FOR(M) {
    int u, v;
    cin >> u >> v;
    --u, --v;
    G[u].emplace_back(v), G[v].emplace_back(u);
  }
  string S;
  cin >> S;
  vc<int> X;
  FOR(K) {
    int x;
    cin >> x;
    X.emplace_back(--x);
  }

  // 駒 0 の初期位置:X0
  // この実装では「他の駒」の個数を K としている
  int X0 = X[0];
  X.erase(X.begin());
  --K;

  // 入力ここまで
  // åœæ­¢ãƒžã‚¹ãŒå­˜åœ¨ã—ãªã„å ´åˆ
  if (count(all(S), '1') == 0) {
    vc<int> V = {X0};
    vc<ll> ANS = bfs(N, G, V, [&](int u, int v) -> int { return 1; });
    return out(ANS);
  }

  // 駒 0 が目的達成までに他の駒がターンを消化する回数 n
  // n>=1 のときに 2n+a が利用できるような a の最小値
  vc<ll> A = [&]() -> vc<ll> {
    vc<int> V;
    FOR(v, N) {
      if (S[v] != '1') continue;
      for (int w: G[v]) { V.emplace_back(w); }
    }
    vc<ll> dist = bfs(N, G, V, [&](int u, int v) -> int { return 1; });
    vc<ll> A(K);
    FOR(k, K) {
      ll x = dist[X[k]];
      A[k] = x - 1;
    }
    return A;
  }();

  // n>=1 のときに n+b が利用できるような b の最小値
  vc<ll> B = [&]() -> vc<ll> {
    vc<int> V;
    FOR(v, N) {
      if (S[v] != '1') continue;
      for (int w: G[v]) {
        if (S[w] == '1') {
          V.emplace_back(v);
          break;
        }
      }
    }
    if (V.empty()) return vc<ll>(K, INF);
    vc<ll> dist = bfs(
        N, G, V, [&](int u, int v) -> int { return (S[u] == '1' ? 0 : 1); });
    vc<ll> B(K);
    FOR(k, K) { B[k] = dist[X[k]]; }
    return B;
  }();

  // 他の駒すべてを n 回ずつターン消費するためにかかるコスト
  vc<ll> COST(N);
  {
    vc<ll> C0(N + 1), C1(N + 1);
    FOR(k, K) {
      ll a = A[k], b = B[k];
      assert(a <= b);
      ll n = b - a;
      chmin(n, N);
      // FOR(x, n) COST[x] += 2 * x + a;
      // FOR(x, n, N) COST[x] += x + b;
      // c1x +c0
      C1[0] += 2, C1[n] -= 2, C0[0] += a, C0[n] -= a;
      C1[n] += 1, C1[N] -= 1, C0[n] += b, C0[N] -= b;
    }
    FOR(n, N) C1[n + 1] += C1[n], C0[n + 1] += C0[n];
    FOR(x, 1, N) COST[x] = C1[x] * x + C0[x];
  }

  auto solution_1 = [&]() -> vc<ll> {
    // K が大きい
    // 再行動回数を枝狩りする
    vc<ll> ANS(N, INF);
    vc<int> V = {X0};

    // min_cnt: v に到達するまでに他のプレイヤがターン消化する最小回数
    vc<ll> min_cnt = bfs(N, G, V, [&](int u, int v) -> int {
      return (S[u] == '1' && u != X0 ? 1 : 0);
    });
    ll LIM = N / K + 1;
    chmin(LIM, count(all(S), '1'));

    // dp[i][v]: v に到達するまでに他のプレイヤが
    // min_cnt[v] + i 回ずつターン消化
    vc<vc<int>> dp(LIM, vc<int>(N, 1e9));
    vc<int> min_cnt_now(N, N);
    deque<pair<int, int>> que;
    dp[0][X0] = 0, que.emplace_back(0, X0);
    while (len(que)) {
      auto [k, v] = POP(que);
      for (int w: G[v]) {
        int k1 = k + (S[v] == '1' && v != X0 ? 1 : 0);
        int x = dp[k - min_cnt[v]][v];
        // 最適になりえないパターンを枝狩りする
        if (!chmin(min_cnt_now[w], k1)) continue;
        if (k1 - min_cnt[w] >= LIM) continue;
        if (N <= x + 1 + ll(K) * (k1 - min_cnt[w])) continue;
        if (chmin(dp[k1 - min_cnt[w]][w], x + 1)) { que.emplace_back(k1, w); }
      }
    }
    FOR(k, LIM) {
      FOR(v, N) {
        ll x = dp[k][v];
        ll n = min_cnt[v] + k;
        if (n < N) chmin(ANS[v], x + COST[n]);
      }
    }
    return ANS;
  };

  auto solution_2 = [&]() -> vc<ll> {
    // 直線の種類ごとに解く
    vc<ll> ANS(N, INF);
    vc<vc<ll>> dp(2, vc<ll>(N, INF));
    vc<pair<int, int>> que1(N + N), que2(N + N);
    auto solve_ab = [&](ll a, ll b) -> void {
      // 他のプレイヤが n 回ターン消化するときに an+b のコストがかかるとして解く
      // v!=X0 かつ S[v]=='1' から出るときにコスト発生
      // dp[v] := プレイヤ1の回数 + an
      fill(all(dp[0]), INF), fill(all(dp[1]), INF);
      int l1 = 0, r1 = 0, l2 = 0, r2 = 0;
      dp[0][X0] = 0, que1[r1++] = {0, X0};
      while (l1 < r1 || l2 < r2) {
        auto [t, v] = [&]() -> pair<int, int> {
          if (l1 == r1) return que2[l2++];
          if (l2 == r2) return que1[l1++];
          auto [t1, v1] = que1[l1];
          auto [t2, v2] = que2[l2];
          return (dp[t1][v1] < dp[t2][v2] ? que1[l1++] : que2[l2++]);
        }();
        for (auto w: G[v]) {
          if (v == X0 || S[v] == '0') {
            if (chmin(dp[t][w], dp[t][v] + 1)) que1[r1++] = {t, w};
          } else {
            if (chmin(dp[1][w], dp[t][v] + a + 1)) que2[r2++] = {1, w};
          }
        }
      }
      FOR(v, N) {
        chmin(ANS[v], dp[0][v]);
        chmin(ANS[v], dp[1][v] + b);
      }
    };

    int L = 1;
    while (L < N) {
      // COST[L], ..., COST[R-1] 部分をまとめて計算する
      int R = L + 2;
      while (R < N && 2 * COST[R - 1] == COST[R - 2] + COST[R]) { ++R; }
      chmin(R, N);
      if (R == L + 1) {
        solve_ab(0, COST[L]);
      } else {
        ll a = COST[L + 1] - COST[L];
        ll b = COST[L] - a * L;
        solve_ab(a, b);
      }
      L = R;
    }
    return ANS;
  };

  vc<ll> ANS = (K >= 200 ? solution_1() : solution_2());
  out(ANS);
}

signed main() {
  solve();
  return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...