ICPC2023 合肥区域赛
# E - Matrix Distances
# Question
有一个大小为 的矩阵,每个单元格填充了颜色值 。计算所有具有相同颜色的单元格对之间曼哈顿距离的总和。
具体而言,请计算:
这里, 表示所有不同颜色的集合。 表示颜色等于指定颜色 的坐标集合 。
# Solution
很显然,需要一个一个颜色计算,对于一个颜色 发现 对答案的贡献是独立的,所以可以拆开来计算
考虑如何计算 对答案的贡献
先将 从小到大排序,计算前缀和数组 ,当枚举到一个 ,有 个 比当前的 小,对答案的贡献就是 ,同时,有 个 比当前的 大,对答案的贡献就是 ,其中 表示当前颜色方块的个数
和 的计算方法相同
# Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
int main() {
ios::sync_with_stdio(false);
int n, m; cin >> n >> m;
vector<vector<int>> a(n + 1, vector<int>(m + 1));
vector<int> b;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
b.push_back(a[i][j]);
}
}
sort(b.begin(), b.end());
int cnt = unique(b.begin(), b.end()) - b.begin();
vector<vector<int>> X(cnt + 1), Y(cnt + 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = lower_bound(b.begin(), b.begin() + cnt, a[i][j]) - b.begin() + 1;
X[a[i][j]].push_back(i);
Y[a[i][j]].push_back(j);
}
}
long long ans = 0;
auto calc = [&] (vector<int>& v) {
int n = v.size();
if (n == 0) return 0ll;
vector<long long> sum(n);
sort(v.begin(), v.end());
sum[0] = v[0];
for (int i = 1; i < n; i++) sum[i] = sum[i - 1] + v[i];
long long ret = 0;
for (int i = 0; i < n; i++) {
ret += 1ll * v[i] * (i + 1) - sum[i];
ret += sum[n - 1] - sum[i] - 1ll * v[i] * (n - (i + 1));
}
return ret;
};
for (auto v : X) ans += calc(v);
for (auto v : Y) ans += calc(v);
cout << ans << '\n';
return 0;
}
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
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
# F - Colorful Balloons
# Solution
签到题
# Code
#include<bits/stdc++.h>
using namespace std;
int n, mx;
string s, ans;
map<string, int> mp;
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> s;
mp[s] ++;
if(mx < mp[s]){
mx = mp[s];
ans = s;
}
}
if(mx > n / 2) cout << ans << endl;
else cout << "uh-oh" << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# G - Streak Manipulation
# Question
给定一个 01 串,长度为 ,最多可以操作 次,每次操作把一个 变成 ,求不超过 次操作后,第 长的连续为 的串的长度
# Solution
考虑二分答案 ,即在最多 次操作之后,第 长的串的大小为
考虑如何 check,注意到 其实很小,可以被定义到动态规划的状态里面去
定义 表示:前 个字符,有 段长度大于等于 的连续 串,且当前字符是否位于第 段连续 串的末尾,所需的最少操作次数
考虑状态转移
-
-
- 此外,在 且 时
其中, 可以用前缀和数组快速求出
总时间复杂度为
# Code
#include <bits/stdc++.h>
using namespace std;
constexpr int INF = 0x3f3f3f3f;
const int MAXN = 2e5 + 5;
int dp[MAXN][6][2];
int main() {
ios::sync_with_stdio(false);
int n, m, k; cin >> n >> m >> k;
string s; cin >> s; s = " " + s;
vector<int> pre(n + 1, 0);
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] + (s[i] == '0');
int l = 0, r = n + 1;
auto check = [&] (int x) {
for (int i = 0; i <= n; i++)
for (int j = 0; j <= k; j++)
dp[i][j][0] = dp[i][j][1] = INF;
dp[0][0][0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= k; j++) {
if (s[i] == '0') {
dp[i][j][0] = min({dp[i][j][0], dp[i - 1][j][0], dp[i - 1][j][1]});
dp[i][j][1] = min({dp[i][j][1], dp[i - 1][j][1] + 1});
}
else {
dp[i][j][0] = min({dp[i][j][0], dp[i - 1][j][0]});
dp[i][j][1] = min({dp[i][j][1], dp[i - 1][j][1]});
}
if (i >= x && j > 0)
dp[i][j][1] = min({dp[i][j][1], dp[i - x][j - 1][0] + pre[i] - pre[i - x]});
}
return min(dp[n][k][0], dp[n][k][1]) <= m;
};
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (check (mid)) l = mid;
else r = mid;
}
if (l <= 0) cout << -1 << '\n';
else cout << l << '\n';
return 0;
}
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
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
# J - Takeout Delivering
# Question
给你一个无向连通图,定义最短路为路径中边权最大的两条边的边权和,求 到 的最短路
# Solution
当时考虑了很久二分,后来发现方向错了
我们需要求一条路径上的路径最大值和次大值的和
那么我们枚举每一条边,假设这条边就是路径上的最大值,那么次大值就应该出现在从 或者
提前用 dijkstra 提前预处理出 的最小路径最大值,同理也能处理出 的最小路径最大值
# Code
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9 + 10;
int main() {
freopen ("J.in", "r", stdin);
ios::sync_with_stdio(false);
int n, m; cin >> n >> m;
vector<vector<pair<int, int>>> g(n + 1);
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
auto dijkstra = [&] (int s) {
vector<int> dis(n + 1, INF);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
vector<int> vis(n + 1, 0);
dis[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto [v, w] : g[u]) {
int tmp = max(dis[u], w);
if (tmp < dis[v]) {
dis[v] = tmp;
pq.push({dis[v], v});
}
}
}
return dis;
};
auto dis1 = dijkstra(1);
auto disn = dijkstra(n);
int ans = INF + INF;
for (int u = 1; u <= n; u++) {
for (auto [v, w] : g[u]) {
if (w >= max(dis1[u], disn[v]))
ans = min(ans, max(dis1[u], disn[v]) + w);
if (w >= max(dis1[v], disn[u]))
ans = min(ans, max(dis1[v], disn[u]) + w);
}
}
cout << ans << '\n';
return 0;
}
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
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
# summary
对于这种需要维护一个最大值和一个次大值
- 考虑开一个
pair<int,int>
或者什么结构,使用最大值和次大值只增不减的特性 - 考虑二分其中一个
- 考虑枚举其中一个,看能否快速求出另外一个
上次更新: 2024/10/30, 18:42:16