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;
}