P11514 [CCO 2024] Treasure Hunt
题目背景
题目描述
佩里海盗正在航行七大洋!他有一张由 N N N 个岛屿和 M M M 条海路组成的地图。第 i i i 条海路连接岛屿 a i a_i ai 和 b i b_i bi,双向通行的费用为 c i c_i ci 金币。与海怪交战代价不菲。为了寻找下一个大笔战利品,佩里已经侦查过每个岛屿,确定第 i i i 个岛上有一个装有 v i v_i vi 金币的宝箱。
他打算航行一段(可能为空)的海路,从岛屿 x x x 出发到岛屿 y y y 结束。旅程结束时,他会打开岛屿 y y y 的宝箱并获得战利品。这里的问题是:佩里并不知道自己当前在哪个岛上!因此,对于每个可能的起始岛 x x x,他想知道从该岛出发所有可能旅程中能够获得的最大净收益(假设他有足够的金币支付沿途的通行费用;他只关心下一次旅程的净利润)。
输入格式
第一行两个整数 n , m n, m n,m。
第二行 n n n 个整数 v i v_i vi。
往后 m m m 行每行三个整数 a i , b i , c i a_i, b_i, c_i ai,bi,ci,其描述了一条海路 i i i。
保证无重边无自环。
输出格式
n n n 行,每行一个整数,第 i i i 行的整数代表以点 i i i 为起点岛屿所获得的最大可能的硬币数。
输入输出样例 #1
输入 #1
4 5
6 5 9 2
1 2 0
3 2 3
4 3 6
1 3 5
2 4 2
输出 #1
6
6
9
4
说明/提示
数据范围
| Subtask | 分数 | 约束 |
|---|---|---|
| Subtask 0 0 0 | 0 0 0 | 样例 |
| Subtask 1 1 1 | 20 20 20 | 1 ≤ n ≤ 3000 1 \le n \le 3000 1≤n≤3000, 0 ≤ m ≤ 3000 0 \le m \le 3000 0≤m≤3000 |
| Subtask 2 2 2 | 20 20 20 | 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1≤n≤106, 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0≤m≤106,对于每个岛屿 i i i, c i = 0 c_i=0 ci=0 或 c i = 10 9 c_i=10^9 ci=109 |
| Subtask 3 3 3 | 28 28 28 | 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1≤n≤106, 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0≤m≤106,任意一对岛屿之间恰好有一条路径 |
| Subtask 4 4 4 | 32 32 32 | 无特殊限制 |
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 10 6 1 \le n \le 10^6 1≤n≤106, 0 ≤ m ≤ 10 6 0 \le m \le 10^6 0≤m≤106, 0 ≤ v i , c i ≤ 10 9 0 \le v_i,c_i \le 10^9 0≤vi,ci≤109, 1 ≤ a i , b i ≤ n 1 \le a_i,b_i \le n 1≤ai,bi≤n。
样例解释 #1

对于第一个和第三个岛屿,最好待在岛上并打开岛上的箱子。
对于第二个岛屿,佩里可以前往第一个岛屿并打开那里的箱子。这将带来 6 − 0 = 6 6 - 0 = 6 6−0=6 枚硬币的净收益,是最佳的净收益。
对于第四个岛屿,佩里可以前往第二个和第三个岛屿并打开那里的箱子。这将带来 9 − 2 − 3 = 4 9 - 2 - 3 = 4 9−2−3=4 枚硬币的净收益,是最佳的净收益。
C++实现
#include <bits/stdc++.h>
using namespace std;
#define pi pair<int, int>
const int N = 3000001; //这里是无向图 + 超级源点 所以要开 n+m*2
int cnt, h[N], nex[N], to[N], w[N]; //链式前向星存图
int n, m, dis[N];
bool vi[N];
void ad(int u, int v, int ww) {
nex[++cnt] = h[u];
to[cnt] = v;
w[cnt] = ww;
h[u] = cnt;
}
void dj(int s) { //模板 这里不多说
memset(dis, 0x3f, sizeof dis);
memset(vi, 0, sizeof vi); //memset常数大但我爱用
priority_queue<pi, vector<pi>, greater<pi> > q;
q.push({0, s});
while(!q.empty()) {
int u=q.top().second, d=q.top().first;
q.pop();
if(vi[u]) continue;
vi[u] = 1;
for(int i=h[u]; i; i=nex[i]) {
int v=to[i], ww=w[i];
if(d+ww < dis[v]) {
dis[v] = d+ww;
q.push({dis[v], v});
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1, ww; i<=n; i++)
scanf("%d", &ww), ad(0, i, -ww); //超级源点
while(m--) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
ad(a, b, c); ad(b, a, c); //由于是无向图所以要建双向边
}
dj(0); //从超级源点开始跑
for(int i=1; i<=n; i++)
printf("%d\n", -dis[i]);
return 0;
}

后续
接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容
用C++实现信奥题 P11514 CCO 2024 Treasure Hunt&spm=1001.2101.3001.5002&articleId=166216907&d=1&t=3&u=d2884d1413fb41028e406a4f9b37b302)
564

被折叠的 条评论
为什么被折叠?



