2024初秋集训——提高组 #40

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;
}
posted @ 2024-10-17 19:37  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报