ABC338

基本情况

A忘记大小写敏感卡了20分钟,BC秒了,E用树状数组草过去了,D错了25个点,似乎是交界没有判断好。

B - Frequency

B - Frequency (atcoder.jp)

这题还可以更优雅点。

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

D - Island Tour (atcoder.jp)

我的想法是贪心选取每一对相邻点的最短路,然后最后判断断点是否会影响答案,会的话贪心的替换最优的另一条路。

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

E - Chords (atcoder.jp)

先破环为链,题目就转变成一个区间内有没有线段相交。

所以做法就是按左端点排序,判断所有区间是不是有完全被包含进去或者完全没有关系。

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;
}
posted @ 2024-01-28 12:04  加固文明幻景  阅读(108)  评论(0编辑  收藏  举报