视频加载失败

安吉D9-T4

2016 字
10 分钟
安吉D9-T4
原题呈现

P5100 足球 / Soccer#

题目描述#

你是 JOI 联赛中一所声名卓著的足球俱乐部的经理.

俱乐部有 NN 名球员,编号为 1N1\ldots N.球员们每天都刻苦地进行训练,剑指联赛冠军.足球场可视为一个底为 WW 米,高 HH 米的长方形,底平行于东西方向,高平行于南北方向.如果某个点向北走 ii 米,再向西走 jj 米恰好到达球场的西北角,这个点可用坐标 (i,j)(i, j) 来表示.

练习结束后,你要回收练习用的足球.开始回收时,所有球员都在足球场上,球员 i(1iN)i (1\leqslant i\leqslant N) 位于 (Si,Ti)(S_i, T_i),球在球员 11 脚下.你正和球员 NN 一起站在 (SN,TN)(S_N, T_N),并准备回收球.球员们把球传到 (SN,TN)(S_N, T_N) 时,你才会回收球.

你可以指挥球员,但某些操作会提升球员的疲劳度.一个球员不能同时进行多项操作. 你可以指挥控球的球员进行如下操作:

  • 踢球.在东西南北四个方向中任选一个,并指定一个正整数 pp,该球员将球朝指定方向踢出恰好 pp 米.假定球滚动时可以穿过其他球员.该球员不会移动,且自动停止控球,疲劳度上升 A×p+BA\times p+B
  • 运球.在东西南北四个方向中任选一个,该球员带球,朝指定方向移动 11 米.该球员仍然控球,疲劳度上升 CC
  • 停止控球.该球员的疲劳度不改变.

你可以指挥没有控球的球员进行如下操作:

  • 移动.在东西南北四个方向中任选一个,该球员朝指定方向移动 11 米,疲劳度上升 CC
  • 控球.如果该球员所在的位置恰好有球,且没有其他球员控球,该球员才能控球.该球员的疲劳度不改变.

球员和球有可能跑出场外,一个位置上可能有多个球员. 一天的训练结束后,球员们非常疲惫.你想知道在回收球的过程中,所有球员上升的疲劳度之和的最小值.

输入格式#

第一行有两个整数 H,WH, W,用空格分隔. 第二行有三个整数 A,B,CA, B, C,用空格分隔. 第三行有一个整数 NN. 在接下来的 NN 行中,第 ii(1iN)(1\leqslant i\leqslant N) 有两个整数 Si,TiS_i, T_i,用空格分隔. 输入的所有数的含义见题目描述.

输出格式#

一行,一个整数,表示在回收球的过程中,所有球员上升的疲劳度之和的最小值.

输入输出样例 #1#

输入 #1#

6 5
1 3 6
3
1 1
0 4
6 5

输出 #1#

26

输入输出样例 #2#

输入 #2#

3 3
0 50 10
2
0 0
3 3

输出 #2#

60

输入输出样例 #3#

输入 #3#

4 3
0 15 10
2
0 0
4 3

输出 #3#

45

输入输出样例 #4#

输入 #4#

4 6
0 5 1000
6
3 1
4 6
3 0
3 0
4 0
0 4

输出 #4#

2020

说明/提示#

样例解释 1#

在这组样例中,球场、球员、球处于如图所示的状态.图中,黑框空心圆圈表示球员,实心圆表示球,你在 (6,5)(6,5)

最优解如下:

  1. 球员 11 把球向东踢出 33 米.疲劳度上升了 1×3+3=61\times 3+3=6,球移动到 (1,4)(1,4)
  2. 球员 22 向南移动 11 米.疲劳度又上升了 66
  3. 球员 22 开始控球.
  4. 球员 22 向东运球 11 米.疲劳度又上升了 66
  5. 球员 22 把球向南踢出 55 米,疲劳度上升了 1×5+3=81\times 5+3=8,球移动到 (6,5)(6,5)

此时,疲劳度之和为 6+6+6+8=266+6+6+8=26.没有更好的方案.

样例解释 2#

在最优解中,不需要踢球.

样例解释 4#

注意这组样例中有多个球员在同一位置的情况.

数据范围与提示#

对于 5%5\% 的数据,N=2N=2. 对于另外 30%30\% 的数据,N1000,A=0N\leqslant 1000, A=0. 对于所有数据, 1H,W500,0A,B,C109,2N105,0SiH,0TiW(1iN),(S1,T1)(SN,TN)1\leqslant H,W\leqslant 500, 0\leqslant A, B, C\leqslant 10^9, 2\leqslant N\leqslant 10^5, 0\leqslant S_i\leqslant H, 0\leqslant T_i\leqslant W(1\leqslant i\leqslant N), (S_1, T_1)\neq(S_N, T_N)

注意到 NN 的范围达到了 10510^5,且球员时时刻刻都在跑动,无法记录球员的状态.于是可以反过来思考:我们可以记录球的状态.由于 HHWW 都小于 500,因此,可以对于每一个 (x,y)(x,y) 满足 xH,yWx\leq H,y\leq W 都视为图上的一个点,最多只有 250000 个点.

对于球,其有两大类状态,一是球所在的位置;二是球的运动状态,关于运动状态,有以下三类:

  • 被人持有,记为 0
  • 水平自主移动,记为 1
  • 横向自主移动,记为 2

依次对于这几种状态之间的转移进行考虑:

  • 000\Rightarrow0 即某一个人向一个方向运球.其转移为:
(x,y,0)C(x±1,y,0)(x,y,0) \xrightarrow{C}(x\pm1,y,0) (x,y,0)C(x,y±1,0)(x,y,0) \xrightarrow{C}(x,y\pm1,0)
  • 01/20\Rightarrow1/2 当一个球被踢出去:
(x,y,0)B(x,y,1/2)(x,y,0)\xrightarrow{B}(x,y,1/2)
  • 111\Rightarrow 1222\Rightarrow 2 (被踢出去之后的)球自己移动一步:
(x,y,1)A(x±1,y,1)(x,y,1)\xrightarrow{A}(x\pm1,y,1) (x,y,2)A(x,y±1,2)(x,y,2)\xrightarrow{A}(x,y\pm1,2)
  • 1/201/2\Rightarrow 0 有一个人接住了这个球.此时需要距离这个位置最近的球员跑过来接球.不难证明,若是一名球员接两次必定不优,因此,选择初始位置距离 (x,y)(x,y) 最近的球员.这个可以使用多源 BFS 预处理,设该球员距离球的距离为 disdis,则转移方程为:
(x,y,1/2)dis×C(x,y,0)(x,y,1/2)\xrightarrow{dis\times C} (x,y,0)

关于上述 1/201/2\Rightarrow 0 中的 BFS 预处理,一般的,我们可以通过以下的步骤来进行处理.使用一个队列:

  • 首先,将所有的球员所在的位置入队.
  • 取出队首的球员位置,让这位球员(设为 ii)向前后左右都移动一格(如果这个格子已经别的球员移动到过,则不朝这个方向移动),记录落点(设为 kk),则离点 kk 最近的球员即为球员 ii.将点 kk 加入队列,重复这一步,直到队列清空.

依照上面几个转移方程建边,再跑一遍 dijkstra 即可.

标程

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 100;
const int MAXPT = 5 * 500 * 500 + 100;
int h, w, a, b, c, n, dist[MAXPT], vis[MAXPT];
pii pos[N];
queue<pair<int, pii>> bfs_q;
// [v, cost]
vector<pii> g[MAXPT];
int near[MAXPT];
struct Node {
int v, d;
friend bool operator<(const Node a, const Node b) {
return a.d > b.d; // 小根堆
}
};
int pt(int a, int x, int y) {
return ((a) * (w + 1) * (h + 1) + (x) * (w + 1) + (y));
}
int pt(int x, int y) { return pt(0, x, y); }
void bfs_upd(int pl, int x, int y) {
if (!near[pt(x, y)]) {
near[pt(x, y)] = pl;
bfs_q.push({pl, {x, y}});
}
}
void bfs() {
while (!bfs_q.empty()) {
auto top = bfs_q.front();
bfs_q.pop();
int pl = top.first, x = top.second.first, y = top.second.second;
if (x + 1 <= h)
bfs_upd(pl, x + 1, y);
if (x - 1 >= 0)
bfs_upd(pl, x - 1, y);
if (y + 1 <= w)
bfs_upd(pl, x, y + 1);
if (y - 1 >= 0)
bfs_upd(pl, x, y - 1);
}
}
int dijkstra(int t) {
memset(dist, 0x3f, sizeof(dist));
memset(vis, 0, sizeof(vis));
priority_queue<Node> pq;
dist[t] = 0;
pq.push({t, dist[t]});
while (!pq.empty()) {
int u = pq.top().v;
pq.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i].first;
int w = g[u][i].second;
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
pq.push({v, dist[v]});
}
}
}
return dist[pt(0, pos[n].first, pos[n].second)];
}
signed main() {
cin >> h >> w;
cin >> a >> b >> c;
cin >> n;
for (int i = 1; i <= n; i++) {
int s, t;
cin >> s >> t;
pos[i] = {s, t};
bfs_q.push({i, pos[i]});
near[pt(s, t)] = i;
}
bfs();
for (int x = 0; x <= h; x++) {
for (int y = 0; y <= w; y++) {
if (x + 1 <= h) {
g[pt(0, x, y)].push_back({pt(0, x + 1, y), c});
g[pt(1, x, y)].push_back({pt(1, x + 1, y), a});
g[pt(0, x, y)].push_back({pt(1, x, y), b});
}
if (x - 1 >= 0) {
g[pt(0, x, y)].push_back({pt(0, x - 1, y), c});
g[pt(1, x, y)].push_back({pt(1, x - 1, y), a});
g[pt(0, x, y)].push_back({pt(1, x, y), b});
}
if (y + 1 <= w) {
g[pt(0, x, y)].push_back({pt(0, x, y + 1), c});
g[pt(2, x, y)].push_back({pt(2, x, y + 1), a});
g[pt(0, x, y)].push_back({pt(2, x, y), b});
}
if (y - 1 >= 0) {
g[pt(0, x, y)].push_back({pt(0, x, y - 1), c});
g[pt(2, x, y)].push_back({pt(2, x, y - 1), a});
g[pt(0, x, y)].push_back({pt(2, x, y), b});
}
int nPl = near[pt(x, y)];
int dist = abs(x - pos[nPl].first) + abs(y - pos[nPl].second);
g[pt(1, x, y)].push_back({pt(0, x, y), c * dist});
g[pt(2, x, y)].push_back({pt(0, x, y), c * dist});
}
}
cout << dijkstra(pt(0, pos[1].first, pos[1].second));
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D9-T4
https://blog.jerrylab.top/posts/problem/anji2026/D9/T4/
作者
Jerry
发布于
2026-08-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0