2024初秋集訓——提高組 #40

Yaosicheng124發表於2024-10-17

B. 艱難睡眠

題目描述

一天有 \(M\) 分鐘,依次編號 \(1,2,\dots,M\)。有 \(N\) 個人,第 \(i\) 個人會在 \(A_i\) 分鐘開始吵鬧,持續 \(B_i\) 分鐘(可能會到第二天)。現在你想要睡連續 \(k\) 分鐘,所以你要使得這 \(k\) 分鐘內沒人吵鬧。你可以花費 \(c_{i,j}\) 的代價讓第 \(i\) 個人從 \(j\) 分鐘開始吵鬧。求你至少需要多少代價才能睡覺,或者確定你不能睡覺。

思路

顯然只有存在 \(B_i>M-k\) 時,你才不能睡覺。

首先列舉在什麼時候睡覺,我們可以對於每個人求出他在什麼時間段不會吵到睡覺,使用 st 表求出這個時間段內的最小需要代價即可。

時空複雜度均為 \(O(NM\log M)\)

程式碼

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

const int MAXN = 5001, MAXM = 2001;

int n, m, k, a[MAXN], log_2[MAXM], ans = int(1e9);
short st[MAXN][14][MAXM];

int Getmin(int i, int l, int r) {
  return min(st[i][log_2[r - l + 1]][l], st[i][log_2[r - l + 1]][r - (1 << log_2[r - l + 1]) + 1]);
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> m >> k;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i] >> a[i];
  }
  log_2[1] = 0;
  for(int i = 2; i <= m; ++i) {
    log_2[i] = log_2[i / 2] + 1;
  }
  for(int i = 1; i <= n; ++i) {
    for(int j = 0; j < m; ++j) {
      cin >> st[i][0][j];
    }
    for(int j = 1; j <= 13; ++j) {
      for(int k = 0; k < m; ++k) {
        if(k + (1 << j) - 1 < m) {
          st[i][j][k] = min(st[i][j - 1][k], st[i][j - 1][k + (1 << (j - 1))]);
        }
      }
    }
  }
  for(int i = 1; i <= n; ++i) {
    if(a[i] > m - k) {
      cout << -1;
      return 0;
    }
  }
  for(int i = 0; i < m; ++i) {
    int res = 0;
    bool ok = 1;
    for(int j = 1; j <= n; ++j) {
      int l = (i + k) % m, r = (i - a[j] + m) % m;
      if(l <= r) {
        res += Getmin(j, l, r);
      }else {
        res += min(Getmin(j, 0, r), Getmin(j, l, m - 1));
      }
    }
    ans = min(ans, res);
  }
  cout << ans;
  return 0;
}

C. 路徑難題

題目描述

你在一個城市中,可以看作一個 \(N\) 個點 \(M\) 條邊的圖,其中有一些邊連線這些點,你有兩種移動方式:

  • 使用計程車移動。每次移動 \(l\) 個單位需要 \(\lceil \frac{l}{r}\rceil\) 的代價。
  • 使用公交車移動。有 \(k\) 種線路,第 \(i\) 個線路可以讓你在 \(a_{i,1},a_{i,2},\dots,a_{i,t_i}\) 之間移動,每次移動需要 \(c_i\) 的代價。

\(Q\) 次查詢,每次查詢從 \(a_i\rightarrow b_i\) 的最小代價。

思路

對於公交車,我們可以建一箇中轉站 \(x_i\),並建邊 \(a_{i,j}\overset{c_i}\rightarrow x_i,x_i\overset{0}\rightarrow a_{i,j}\) 即可。

由於這裡 \(Q\le 10\),所以對於每次查詢暴力跑最短路。最短路中有兩個屬性:當前計程車走了多遠,總共花費了多少代價。

當總代價一樣時,很明顯計程車行走距離 \(\bmod r\) 更小的之後更優。

空間複雜度 \(O(N+M+K)\),時間複雜度 \(O((N+M+K)\log (N+K))\)

程式碼

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;

const int MAXN = 210001;
const ll INF = (ll)(1e18);

int r;

struct Node {
  int u;
  ll dis, cost;
  ll Calc() const {
    return cost + (dis + r - 1) / r;
  }
  bool operator<(const Node &b) {
    return Calc() < b.Calc() || (Calc() == b.Calc() && dis % r < b.dis % r);
  }
};

struct cmp {
  bool operator()(const Node &a, const Node &b) const {
    return a.Calc() > b.Calc() || (a.Calc() == b.Calc() && a.dis % r > b.dis % r);
  }
};

struct INFO {
  ll dis, cost;
  ll Calc() const {
    return cost + (dis + r - 1) / r;
  }
  bool operator<(const INFO &b) {
    return Calc() < b.Calc() || (Calc() == b.Calc() && dis % r < b.dis % r);
  }
}dist[MAXN];

int n, m, k, q;
bool vis[MAXN];
vector<pii> e[MAXN];

ll dij(int s, int t) {
  priority_queue<Node, vector<Node>, cmp> pq;
  for(int i = 1; i <= n + k; ++i) {
    dist[i] = {INF, INF};
    vis[i] = 0;
  }
  pq.push({s, 0, 0}), dist[s] = {0, 0};
  for(; !pq.empty(); ) {
    auto [u, dis, cost] = pq.top();
    pq.pop();
    if(u == t) {
      return cost + (dis + r - 1) / r;
    }
    if(vis[u]) {
      continue;
    }
    vis[u] = 1;
    for(auto [v, w] : e[u]) {
      ll nowd = (u <= n && v <= n ? dis + w : 0ll), nowc = cost + (u <= n && v <= n ? 0ll : w + (dis + r - 1) / r);
      if(INFO{nowd, nowc} < dist[v]) {
        dist[v] = INFO{nowd, nowc};
        pq.push({v, nowd, nowc});
      }
    }
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> m >> k >> r >> q;
  for(int i = 1, u, v, w; i <= m; ++i) {
    cin >> u >> v >> w;
    e[u].emplace_back(v, w);
    e[v].emplace_back(u, w);
  }
  for(int i = 1, t, c; i <= k; ++i) {
    cin >> t >> c;
    for(int j = 1, x; j <= t; ++j) {
      cin >> x;
      e[x].emplace_back(n + i, c);
      e[n + i].emplace_back(x, 0);
    }
  }
  for(int i = 1, u, v; i <= q; ++i) {
    cin >> u >> v;
    cout << dij(u, v) << "\n";
  }
  return 0;
}

相關文章