vp 2023 icpc 合肥 解题补题记录 [F E J G]
gym 链接: https://codeforces.com/gym/104857
F. Colorful Balloons
血签, 用 map 存一下每个颜色气球出现的次数, 找出出现次数大于一半的颜色.
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
signed main() {
int n;
cin >> n;
string s;
map<string, int>ma;
for (int i = 1; i <= n; i++) {
cin >> s;
ma[s]++;
}
for (auto c : ma) {
if (c.second * 2 > n) {
cout << c.first << endl;
return 0;
}
}
cout << "uh-oh" << endl;
}
E. Matrix Distances
把每个颜色的坐标存到一个集合里, 在把 x, y 轴分开存, 打一个前缀和, 就能快速求所有的曼哈顿距离
#define rep(i,a,b) for (int i = a; i <= b; ++i)
#define per(i,a,b) for (int i = a; i <= b; --i)
int n, m, c;
unordered_map<int, vector<int> >xma, yma;
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
rep(i, 1, n) {
rep(j, 1, m) {
cin >> c;
xma[c].emplace_back(i);
yma[c].emplace_back(j);
}
}
for (auto &[clo, v] : xma)sort(v.begin(), v.end());
for (auto &[clo, v] : yma)sort(v.begin(), v.end());
long long ans = 0;
for (auto &[clo, v] : xma) {
long long pre = 0, cnt = 0;
for (auto &c : v) {
cnt++;
pre += c;
ans += cnt * c - pre;
}
}
for (auto &[clo, v] : yma) {
long long pre = 0, cnt = 0;
for (auto &c : v) {
cnt++;
pre += c;
ans += cnt * c - pre;
}
}
cout << ans * 2 << endl;
}
J. Takeout Delivering
先用 djs 跑出来 1 到每个点路径上最大边的最小值, 再跑一下 n 到每个点路径上最大边的最小值.
再枚举每个边作为 1 到 n 路径上的最大边, 往两边看, 次大边是否符合, 并更新答案.
#define rep(i,a,b) for (int i = a; i <= b; ++i)
#define per(i,a,b) for (int i = a; i <= b; --i)
#define pii pair<int,int>
const int Maxn = 3e5 + 10;
vector<pii>eg[Maxn];
int Maxeg1[Maxn], Maxeg2[Maxn];
int n, m, v, u, w, vis[Maxn];
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
rep(i, 1, m) {
cin >> v >> u >> w;
eg[v].emplace_back(u, w);
eg[u].emplace_back(v, w);
}
rep(i, 2, n) Maxeg1[i] = INT_MAX;
rep(i, 1, n - 1) Maxeg2[i] = INT_MAX;
priority_queue<pii,vector<pii>, greater<pii> >q;
q.emplace(0, 1);
while (!q.empty()) {
auto [Maxeg, tis] = q.top();
q.pop();
if (vis[tis])continue;
vis[tis] = 1;
Maxeg = Maxeg1[tis];
for (auto [to, w] : eg[tis]) {
if (max(w, Maxeg) < Maxeg1[to]) {
Maxeg1[to] = max(w, Maxeg);
q.emplace(Maxeg1[to], to);
}
}
}
q = priority_queue<pii,vector<pii>, greater<pii> >();
q.emplace(0, n);
rep(i, 1, n)vis[i] = 0;
while (!q.empty()) {
auto [Maxeg, tis] = q.top();
q.pop();
if (vis[tis])continue;
vis[tis] = 1;
Maxeg = Maxeg2[tis];
for (auto [to, w] : eg[tis]) {
if (max(w, Maxeg) < Maxeg2[to]) {
Maxeg2[to] = max(w, Maxeg);
q.emplace(Maxeg2[to], to);
}
}
}
int ans = INT_MAX;
rep(i, 1, n) {
for (auto [to, w] : eg[i]) {
if (Maxeg1[i] > w || Maxeg2[to] > w)continue;
ans = min(ans, w + max(Maxeg1[i], Maxeg2[to]));
}
}
cout << ans << endl;
}
G. Streak Manipulation
二分答案找第 k 长的串的最大长度, check 函数内用 dp.
DP思路: 对于当前的 m i d mid mid, 设 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1] 为前 i i i 个字符,有 j j j 段长度大于等于 m i d mid mid 的连续 1 1 1 串,且当前字符是否位于第 j j j 段连续 1 1 1 串的末尾(0为假1为真)所需的最少操作次数. 转移方式见代码.
int dp[200020][10][2], n, m, k, pre[200010];
signed main()
{
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m >> k;
string s;
cin >> s;
s = ' ' + s;
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1];
if (s[i] == '0')pre[i]++;
}
auto check = [&](int len) -> bool{
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= k; ++j) {
dp[i][j][0] = INT_MAX / 2;
dp[i][j][1] = INT_MAX / 2;
}
}
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], min(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 >= len && j > 0) {
dp[i][j][1] = min(dp[i][j][1], dp[i - len][j - 1][0] + pre[i] - pre[i - len]);
}
}
}
return min(dp[n][k][0], dp[n][k][1]) <= m;
};
int l = 0, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) {
l = mid;
}
else {
r = mid - 1;
}
}
if (!l)l = -1;
cout << l << endl;
}