数据结构作业题解11

数据结构作业题解11

exdoubled Lv4

A.SA. 这就是美国

题目描述

Mr.Trump 创立了一个路网系统,为了推行虚拟货币,他决定使用 BTC 和 ETH 作为所有道路的通行证。

具体而言,对于每一条双向道路 \((u_i, v_i)\),你需要同时拥有至少 \(a_i\) 个 BTC 和至少 \(b_i\) 个 ETH,才能被 Mr.Trump 鉴定为优质美国公民从而获批通过这条路。

需要注意的是,这里的费用是通行证性质的,你并不会上交任何货币。

你现在知道当前 BTC 和 ETH 的价格分别为 \(S, T\) 美元,请问至少需要多少美元才能保证你从任意一个城市出发都能到达其他城市。

输入格式

第一行 4 个整数 \(n, m, S, T\)

接下来 \(m\) 行,每行 4 个整数 \(u_i, v_i, a_i, b_i\)

输出格式

一个整数,表示最小花费。如果无论如何都有城市无法互通,那么输出 \(-1\)

样例

输入样例1

1
2
3
4
5
6
4 5 1 1
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17

输出样例1

1
32

输入样例2

1
2
3 1 998244353 100000007
1 2 1 1

输出样例2

1
-1

数据范围与提示

\(1 \leq n \leq 5 * 10^3\), \(1 \leq m \leq 10^4\)

\(1 \leq u_i, v_i \leq n\)

\(1 \leq a_i, b_i \leq 10^5\)

\(1 \leq S, T \leq 10^9\)


思路与题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
#include <climits>
#include <set>

using namespace std;

struct Edge {
int u, v;
int a, b;
};
vector<Edge> edges;
vector<int> pa;
set<int> as;

bool cmp(Edge e1, Edge e2) {
if(e1.b != e2.b) return e1.b < e2.b;
return e1.a < e2.a;
}

int find(int u) {
if (u == pa[u]) return u;
return pa[u] = find(pa[u]);
}

int main() {
int n,m,S,T;
cin >> n >> m >> S >> T;
edges.resize(m);
pa.resize(n + 1);
for (int i = 0; i < m; ++i) {
cin >> edges[i].u >> edges[i].v >> edges[i].a >> edges[i].b;
as.insert(edges[i].a);
}

sort(edges.begin(), edges.end(), cmp);

long long ans = LLONG_MAX;

for (auto ca : as) {
iota(pa.begin(), pa.end(), 0);
int comps = n;
int maxB = -1;
for (auto e : edges) {
if (e.a > ca) continue;
int pu = find(e.u); int pv = find(e.v);
if (pu != pv) {
pa[pv] = pu;
comps--;
if (e.b > maxB) maxB = e.b;
if (comps == 1) break;
}
}

if (comps == 1) {
long long cur = S * (long long) ca + T * (long long) maxB;
if (cur < ans) ans = cur;
}
}

cout << ((ans == LLONG_MAX) ? -1 : ans);


return 0;
}

B. 这就是法国

传统题 1000ms 256MB

Description

众所周知,法国是一个经常罢工的国家。在20xx年,由于对工资过低且工作时间过长表示不满,工会决定在每个城市进行罢工要求政府提高福利待遇。法国政府决定派遣军队镇压罢工活动。已知法国有 n 座城市,第 i 座城市的坐标为 \((x_i, y_i)\)。法国军队要镇压所有城市罢工活动,法国可以空降军队到城市 i 镇压罢工,这样会伤亡 \(c_i\)。法国还可以派遣相邻的罢工活动已经镇压的城市 i 的军队到该城市 j;这样会伤亡 \((k_i + k_j) \times ((|x_i - x_j| + |y_i - y_j|)\)。军队可以在已经被镇压的城市间任意行动不会有任何伤亡。法国军队想要用最小的伤亡镇压所有罢工行为,请问最小伤亡人数有多少?

Format

Input

第一行有一个整数 \(n\),表示城市的数量

接下来 \(n\) 行,每行有两个整数,\(x_i\)\(y_i\) 表示城市的坐标

接下来一行 \(n\) 个整数 \(c_1, c_2, ..., c_n\),表示法国空降军队到城市的伤亡人数

接下来一行 \(n\) 个整数 \(k_1, k_2, ..., k_n\),表示法国派遣军队到相邻城市的伤亡系数(伤亡人数如题目描述中所示)。

Output

输出一个整数,表示最小的伤亡人数。

Samples

输入样例1

1
2
3
4
5
6
3
2 3
1 4
3 2
3 2 3
3 2 3

输出样例1

1
8

输入样例2

1
2
3
4
5
6
3
2 1
1 2
3 3
23 2 23
3 2 3

输出样例2

1
27

Limitation

\(1 \leq n \leq 2000\)

\(1 \leq x_i, y_i \leq 10^6\)

\(1 \leq c_i, k_i \leq 10^9\)


思路与题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <vector>
#include <climits>
#include <queue>
#include <cstdlib>

using namespace std;
using ll = long long;



int main() {
int n;
cin >> n;
vector<ll> x(n), y(n), c(n), k(n);
for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
for (int i = 0; i < n; i++) cin >> c[i];
for (int i = 0; i < n; i++) cin >> k[i];

vector<ll> key(n + 1, LLONG_MAX);
vector<bool> visited(n + 1, false);


auto dist = [&](int i, int j) -> ll {
if (i == n && j < n) return c[j];
if (j == n && i < n) return c[i];
if (i == n && j == n) return 0LL;
return (k[i] + k[j]) * (std::llabs(x[i] - x[j]) + std::llabs(y[i] - y[j]));
};

using Node = pair<ll, int>;

priority_queue<Node, vector<Node>, greater<Node>> pq;
ll ans = 0;

for (int i = 0; i < n; i++) {
key[i] = c[i];
pq.emplace(key[i],i);
}
key[n] = 0;
pq.emplace(0,n);


while (!pq.empty()) {
auto [cur, u] = pq.top(); pq.pop();
if (visited[u] || cur != key[u]) continue;
visited[u] = true;
ans += key[u];

for (int v = 0; v <= n; v++) {
if (visited[v]) continue;
ll curDist = dist(u, v);
if (curDist < key[v]) {
key[v] = curDist;
pq.emplace(key[v], v);
}
}
}
cout << ans;

return 0;
}

C. 这就是俄国1

题目描述

众所周知,俄国的克格勃力量很强大,而且对乌克兰虎视眈眈。因此决定派遣间谍进入乌克兰。乌克兰有 \(n\) 座城市,克格勃在乌克兰的每座城市都成功插入间谍,现在他们需要建立通讯线,使得任意两座城市的间谍都能直接或者通过其他多人转发间接联系得上。已知第 \(i\) 座城市乌克兰的警戒值为 \(a_i\),要在城市 \(u\) 和城市 \(v\) 建立通信线需要花费代价 \(a_u + a_v\)。但是苏联留下了一些遗产,已知有 \(m\) 条苏联克格勃留下的通信线,只需要花费代价 \(w_i\) 就能重启该通信线。现在克格勃需要花费最小代价建立或者重启通信线,使得任意两座城市之间的间谍都能相互联系得上,请问最小代价为多少。

输入格式

第一行包括两个数 \(n\)\(m\),表示乌克兰城市数量和苏联留下的通信线路数量。

第二行包括 \(n\) 个数,表示每座城市乌克兰的警戒值 \(a_i\)

接下来 \(m\) 行,每行包括三个数 \(u_i, v_i, w_i\),表示苏联留下的第 \(i\) 条通信线连接城市 \(u_i\) 和城市 \(v_i\),重启代价为 \(w_i\)

输出格式

一个整数,表示克格勃建立或者重启通信线使得任意两座城市之间的间谍都能相互联系得上的最小代价。

样例

输入样例1

1
2
3
4
3 2
1 3 3
2 3 5
2 1 1

输出样例1

1
5

输入样例2

1
2
4 0
1 3 3 7

输出样例2

1
16

输入样例3

1
2
3
4
5
6
5 4
1 2 3 4 5
1 2 8
2 3 6
1 4 7
1 5 15

输出样例3

1
18

数据范围与提示

\(1 \leq n, m \leq 2 * 10^5\)


思路与题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>


using namespace std;
using ll = long long;

struct Edge {
int u, v;
ll cost;
};

vector<int> pa;
vector<Edge> edges;
vector<ll> a;

int find(int u) {
if (u == pa[u]) return u;
return pa[u] = find(pa[u]);
}

bool cmp(const Edge &a, const Edge &b) {
return a.cost < b.cost;
}

int main() {
int n,m;
cin >> n >> m;
a.resize(n+1);

for (int i = 1; i <= n; i++) cin >> a[i];
int s = 1;
for (int i = 2; i <= n; ++i) if (a[i] < a[s]) s = i;

for (int i = 1; i <= n; ++i) {
if (i == s) continue;
edges.push_back({s, i, a[i] + a[s]});
}

for (int i = 0; i < m; i++) {
int u, v;
ll cost;
cin >> u >> v >> cost;
edges.push_back({u, v, cost});
}
sort(edges.begin(), edges.end(), cmp);

pa.resize(n+1);
iota(pa.begin(), pa.end(),0);

ll ans = 0;

int comps = n;
for (auto e : edges) {
int pu = find(e.u), pv = find(e.v);
if (pu != pv) {
pa[pv] = pu;
comps--;
ans += e.cost;
if (comps == 1) break;
}
}
cout << ans;

return 0;
}

D. 这就是英国

题目描述

众所周知,英国地铁的历史非常悠久,这导致地铁站的环境非常糟糕。现在英国政府决定修缮伦敦的地铁站。伦敦有 \(n\) 个地点和 \(m\) 条地铁,每条地铁都连通两个地铁站。每个地铁站容量为 \(v_i\)。由于英国没有那么庞大的工程队伍,因此他们只能一个一个地铁站修。修缮一个地点的地铁站会导致其他还没有修的地铁站的人非常不满,不满度为 \(v_{i1} + v_{i2} + ... + v_{ik}\),其中 \(i_1, i_2, ..., i_k\) 表示与该地点有地铁相连但尚未修的地铁站。现在英国政府需要减少总不满度,请问总不满度最小为多少?

输入格式

第一行包括两个数 \(n\)\(m\)

第二行包括 \(n\) 个数 \(v_1, v_2, ..., v_n\),表示 \(n\) 个地点的容量。

接下来 \(m\) 行,每行两个数 \(x_i, y_i\),表示 \(x_i\) 地铁站与 \(y_i\) 地铁站相连。

输出格式

一个整数,表示最小的总不满度。

样例

输入样例1

1
2
3
4
4 3
10 20 30 40
1 4
2 3

输出样例1

1
40

输入样例2

1
2
3
4
5
6
4 4
100 100 100 100
1 2
2 3
2 4
3 4

输出样例2

1
400

输入样例3

1
2
3
4
5
6
7
8
9
10
7 10
40 10 20 10 20 80 40
1 5
4 5
5 2
6 7
1 6
1 3
4 3
1 4

输出样例3

1
160

数据范围与提示

\(1 \leq n \leq 1000, 1 \leq m \leq 2000\)

\(1 \leq v_i \leq 10^5\)

第一个样例解释:

英国政府首先修缮 3 号地铁站,2 号地铁站尚未修缮且与 3 号地铁站有地铁相连,造成的不满度为 20

然后修缮 2 号地铁站,1 号和 3 号地铁站与 2 号地铁站有地铁相连,由于 3 号地铁站已经修缮完成,因此造成的不满度为 10


思路与题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>

using namespace std;


int main() {
int n, m;
cin >> n >> m;
vector<long long> v(n + 1);
for (int i = 1; i <= n; ++i) cin >> v[i];

long long ans = 0;
for (int i = 0; i < m; ++i) {
int x, y;
cin >> x >> y;
ans += min(v[x], v[y]);
}
cout << ans;
return 0;
}

E. 嘉然上班要迟到了!

Description

嘉然需要从一张 \(n\) 个点 \(m\) 条边的无向图上从 1 号点跑到 \(n\) 号点。每条边有两个权值 \(t\)\(w\) 分别表示路过的时间和过路费。

嘉然希望选择一条时间最短的路径,同时,鲁迅曾经说过:“有钱不省是**。”,嘉然希望在时间最短的情况下选择一条过路费最小的。

请输出最小时间和过路费。

Format

Input

第一行两个整数 \(n\)\(m\) 分别表示点数和边数。

接下来 \(m\) 行每行 4 个整数 \(x, y, t, w\) 表示节点 \(x\) 和节点 \(y\) 之间有一条通过时间为 \(t\),过路费为 \(w\) 的路径。

Output

一行两个整数表示最小时间和过路费。

Samples

输入样例

1
2
3
4
5
3 4
1 2 2 1
2 3 1 2
1 3 3 5
2 3 1 1

输出样例

1
3 2

Limitation

\(1 \leq n \leq 10^5, 1 \leq m \leq 5 \times 10^5, 1 \leq t, w \leq 10^9\)

保证 1 号点一定能到 \(n\) 号点。

思路与题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <vector>
#include <climits>
#include <queue>
#include <tuple>

using namespace std;
using ll = long long;

struct Edge {
int to;
ll time, cost;
};

bool cmp(ll t1, ll w1, ll t2, ll w2) {
if (t1 != t2) return t1 < t2;
return w1 < w2;
}

int main() {
int n,m;
cin >> n >> m;
vector<vector<Edge>> adj(n);
while (m--) {
int u,v,t,w;
cin >> u >> v >> t >> w;
u--; v--;
adj[u].push_back({v,t, w});
adj[v].push_back({u,t, w});
}

vector<ll> distT(n, LLONG_MAX);
vector<ll> distW(n, LLONG_MAX);

using Node = tuple<ll,ll, int>; // t, w, id
priority_queue<Node, vector<Node>, greater<>> pq;

distT[0] = 0;
distW[0] = 0;

pq.emplace(distT[0],distW[0],0);

while (!pq.empty()) {
auto [curT, curW, curid] = pq.top(); pq.pop();
if (cmp(distT[curid], distW[curid], curT, curW)) continue;
for(auto e : adj[curid]) {
ll nxtT = curT + e.time;
ll nxtW = curW + e.cost;
if (cmp(nxtT, nxtW, distT[e.to], distW[e.to])) {
distT[e.to] = nxtT;
distW[e.to] = nxtW;
pq.emplace(nxtT,nxtW, e.to);
}
}
}

cout << distT[n-1] << ' ' << distW[n-1];
return 0;
}

上机题重复了之前的题,不给出题解,只给出题目

SB. XRJ的钢琴键

传统题 1000ms 256MiB

题目背景

XRJ,人称钢琴小王子,乃是 1-213 寝室四大成员之一。

题目描述

XRJ 的魔法钢琴有 \(n\) 个钢琴键,每个钢琴键有一个音乐值 \(c_i\),这些值可正可负可为 \(0\)

XRJ 手很长,一次可以按下区间 \([L,R]\) 里面的所有钢琴键,区间 \([L,R]\) 的音乐值为区间内钢琴键的音乐值的总和。

他想知道,音乐值大于零的区间到底有多少个?

合法区间 \([L,R]\) 应当满足 \(1 \leq L \leq R \leq n\)

输入格式

一行,一个整数 \(n\)

接下来 \(n\) 行,每行一个整数 \(c_i\),代表第 \(i\) 个钢琴键的音乐值。

输出格式

一行,一个整数,代表音乐值大于零的区间的个数。

样例 #1

样例输入 #1

1
2
3
4
5
4
10
-10
7
-5

样例输出 #1

1
5

提示

100%的数据,\(n \leq 5 \times 10^5\)\(|c_i| \leq 10^7\)

SC. 有天你飞起来去摘那云彩

传统题 2500ms 512MiB

Description

天上有 \(n\) 朵云彩,他们从左到右排成一排,每朵云彩都有一个洁白值,分别是 \(p_1, p_2, \cdots p_n\),并且每朵云彩的洁白值都不同。

现在,你想要飞起来去摘下一些云彩,为此你需要计算一个奇特的任务。

具体的,你需要从每个长度为 \(m\) 的区间中,选出 \(k\) 朵云彩,使得它们的洁白值之和尽可能小。

Format

Input

第一行,三个整数,\(n, m, k\)

第二行,\(n\) 个整数,\(p_1, p_2, \cdots p_n\) 表示云彩的洁白值。

保证云彩的洁白值互不相同。

Output

输出一行,包含 \(n - m + 1\) 个整数,其中第 \(i\) 个整数表示从 \(p_i, p_{i+1}, \cdots p_{i+m-1}\) 中选出恰好 \(k\) 个云彩能得到的最小洁白值总和。

Samples

输入 1

1
2
6 4 3
1 3 4 2 5 6

输出 1

1
6 9 11

输入 2

1
2
10 6 3
1 2 10 9 7 8 4 3 6 5

输出 2

1
10 13 14 13 12

Limitation

\(1 \leq k \leq m \leq n \leq 10^6\)

\(1 \leq p_i \leq n\),对于 \(i \neq j\)\(p_i \neq p_j\)

  • Title: 数据结构作业题解11
  • Author: exdoubled
  • Created at : 2025-12-17 10:00:00
  • Updated at : 2025-12-27 20:23:24
  • Link: https://github.com/exdoubled/exdoubled.github.io.git/sjjg/homework11/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments