ABC338
基本情况
A忘记大小写敏感卡了20分钟,BC秒了,E用树状数组草过去了,D错了25个点,似乎是交界没有判断好。
B - Frequency
这题还可以更优雅点。
int main() {
string s;
cin >> s;
map<char, int> cnt;
for (int i = 0; i < s.size(); i++) {
cnt[s[i]]++;
}
vector<pair<char,int> > a(cnt.begin(), cnt.end());
sort(a.begin(), a.end(), [&](auto& x, auto& y) {
if (x.second == y.second) return x.first < y.first;
return x.second > y.second;
});
cout << a.front().first << endl;
return 0;
}
赛时急着过题,写的很无脑,复杂度有 \(\operatorname O(n\times\log n)\)。
其实这类字母题完全可以照着字母枚举,复杂度直接降低到 \(O(n)\)
int main() {
string s;
cin >> s;
int cnt[26] = {0};
const char d = 'a';
for (char c : s) ++cnt[c - d];
int ans = 0;
for (int i = 0; i < 26; i++) if (cnt[i] > cnt[ans]) {
ans = i;
}
cout << (char)(ans + d) << endl;
}
D - Island Tour
我的想法是贪心选取每一对相邻点的最短路,然后最后判断断点是否会影响答案,会的话贪心的替换最优的另一条路。
int main()
{
int n, m;
cin >> n >> m;
vector<ll> a(m);
ll ans = 0;
for (ll& x : a) cin >> x;
vector<pair<ll, ll> > res;
ll f1_l = inf, f1_r = -1, f2_l = inf, f2_r = -1;
bool f1 = false, f2 = false;
vector<int> cnt1_l(n + 1), cnt1_r(n + 1), cnt2_l(n + 1), cnt2_r(n + 1);
for (int i = 1; i < m; i++) {
ll x = max(a[i], a[i - 1]), y = min(a[i], a[i - 1]);
if (x - y < n - x + y) {
res.emplace_back(make_pair(x - y, n - x + y));
f1_l = min(f1_l, y);
cnt1_l[f1_l]++;
f1_r = max(f1_r, x);
cnt1_r[f1_r]++;
f1 = true;
}
else {
res.emplace_back(make_pair(n - x + y, x - y));
f2_l = min(f2_l, y);
cnt2_l[f2_l]++;
f2_r = max(f2_r, x);
cnt2_r[f2_r]++;
f2 = true;
}
ans += min(x - y, n - x + y);
}
if (f1 && f2 && f2_l >= f1_l && f2_r <= f1_r) {
sort(res.begin(), res.end(), [&](auto& x1, auto& x2) {
return (x1.second - x1.first) < (x2.second - x2.first);
});
ans -= res.front().first;
ans += res.front().second;
}
else if (f1 && !f2 && cnt1_l[f1_l] > 1 || cnt1_r[f1_r] > 1) {
sort(res.begin(), res.end(), [&](auto& x1, auto& x2) {
return (x1.second - x1.first) < (x2.second - x2.first);
});
ans -= res.front().first;
ans += res.front().second;
}
else if (!f1 && f2 && cnt2_l[f2_l] > 1 || cnt2_r[f2_r] > 1) {
sort(res.begin(), res.end(), [&](auto& x1, auto& x2) {
return (x1.second - x1.first) < (x2.second - x2.first);
});
ans -= res.front().first;
ans += res.front().second;
}
cout << ans << endl;
return 0;
}
但是这个方法根本就不对,无法确保当一条路被断掉时,只有一条路径会被影响。
正解就是暴力枚举每一条边断的情况,然后利用差分思想优化。
但我只会差分模板,思想并不深刻,待我复习。
复习回来啦。
- 枚举每一条边断开,利用差分数组 \(v_i\) 表示第 \(i\) 边断开时总的代价。
- 对于每一对 \(a_i, a_{i + 1}\),无非两种走法,要么顺时针,要么逆时针。
- 如果顺时针走法中的路径断了,那就只能逆时针
- 那么就对顺时针区间维护上逆时针走法的代价
- 如果逆时针走法中的路径断了,那就只能顺时针
- 那么就对逆时针区间维护上逆时针走法的代价
- 如果顺时针走法中的路径断了,那就只能逆时针
- 最后差分倒退回来原数组,求最小的 \(v_i\)。
int main()
{
int n, m;
cin >> n >> m;
vector<int> a(m);
for (int& x : a) cin >> x, x--;
vector<ll> v(n + 1);
auto dist = [&](int from, int to) {//计算贡献
if (from <= to) return to - from;
else return to + n - from;
};
auto add = [&](int from, int to, int num) {//维护差分
if (from <= to) {
v[from] += num;
v[to] -= num;
}
else {
v[from] += num; v[n] -= num;
v[0] += num; v[to] -= num;
}
};
for (int i = 0; i + 1 < m; i++) {//A、B路径为顺逆时针
add(a[i], a[i + 1], dist(a[i + 1], a[i]));//如果A路径走不了,把走B路径的代价维护上去
add(a[i + 1], a[i], dist(a[i], a[i + 1]));//如果B路径走不了,把走A路径的代价维护上去
}
ll ans = 1LL << 60;
for (int i = 0; i < n; i++) {
v[i + 1] += v[i];//差分还原原数组
ans = min(ans, v[i]);
}
cout << ans << endl;
return 0;
}
E - Chords
先破环为链,题目就转变成一个区间内有没有线段相交。
所以做法就是按左端点排序,判断所有区间是不是有完全被包含进去或者完全没有关系。
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<std::pair<int, int>> seg(n);
for (auto&[l, r] : seg) {std::cin >> l >> r; if (l > r) {std::swap(l, r);}}
std::sort(all(seg));
std::vector<std::pair<int, int>> stk;
bool ok = false;
for (auto&[l, r] : seg) {
while (not stk.empty() and l > stk.back().second) {stk.pop_back();}//左端点比栈顶右端点还大,说明两个区间完全没有关系
if (not stk.empty() and r > stk.back().second) {ok = true;}//说明当前l<=栈顶r,并且当前r又大于栈顶r,那么就是有相交,即没有被完全包含进去
stk.emplace_back(l, r);
}
std::cout << (ok ? "Yes\n" : "No\n");
return 0;
}