Edu42

基本情况

B 题目读假卡了快半小时,以后一定要彻底读清楚题目,分析好每一种情况再写代码。

D 想了一个解法,但无法保证答案有序,之后思维就死在了那个解法上无法变更。

D. Merge Equals

Problem - D - Codeforces

一失足成千古恨,再回頭是百年身

Mycode

map維護序列,然后能和成就尽量合成

但是答案无法保证顺序符合题目操作后的顺序

void solve() {
    int n;
    std::cin >> n;
    std::map<i64, int> cnt;
    for (int i = 0, x; i < n; i++) {
        std::cin >> x;
        cnt[x]++;
    } 

    auto it(cnt.begin());
    while (it != cnt.end()) {
        auto&[x, y](*it);
        if (y & 1) {
            ++it;
            if (y > 1) {
                cnt[1LL * x * (y - 1)]++;
            }
        }
        else {
            cnt[1LL * x * (y)]++;
            it = cnt.erase(it);
        }
    }
    std::cout << sz(cnt) << '\n';
    for (auto&[x, _] : cnt) {std::cout << x << ' '; }
    std::cout << '\n';
}

300Iq

只有直接模拟原操作才能保证答案的有序,因为从小到大按顺序处理每个数字,处理完之后不会重复处理,所以复杂度其实是 \(O(n)\)​ 的,用 std::map 或者优先队列实现都是可以的

直接模拟的过程中维护合成的新数的下标即可。

void solve() {
    int n;
    std::cin >> n;
    std::map<i64, std::set<int>> cnt;//set维护该数字对应的下标,利用set的特性保证每次取出来的下标都是题目要求的最靠前的
    std::vector<i64> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        cnt[a[i]].insert(i);
    } 

    for (auto&[x, S] : cnt) {
        while (sz(S) > 1) {//直接模拟过程
            int t1(*S.begin());
            int t2(*(++S.begin()));
            a[t2] += a[t1]; 
            a[t1] = -1;//表示这个数被合成了,删除
            S.erase(t1);
            S.erase(t2);
            cnt[a[t2]].insert(t2);//对合成的新数加入对应的下标,这一步保证了答案的有序
        }
    }

    std::vector<i64> res;

    for (auto& x : a) if (x != -1) {
        res.push_back(x);
    }

    std::cout << sz(res) << '\n';

    for (auto& x : res) std::cout << x << ' ';

    std::cout << '\n';

}

E

Problem - E - Codeforces

一道有质量的构造题。

一种错误的做法是,直接将相邻的 \(P,B\)\(P,R\) 相连。

但其实两个 \(P\) 之间的边是可以共用的,就出现了多种连边方案。

以下讨论 \(P,R\) 的情况,因为 \(P,B\) 同理。

对于最左端的 \(P\) 左边的 \(R\) 点,直接与该 \(P\) 点相连,没有其它方案。对于最右端的 \(P\) 点右边的 \(R\) 点同理。

接下来对于两个相邻的 \(P\) 点以及它们之间的 \(R\) 点,有两种连法。

一是与错误解法相同,直接连相邻的 \(P,R\) 点即可,代价等于这两个 \(P\) 点的距离。

二是先连两个 \(P\) 点,然后将其间相邻的 \(R\) 点连接为两个连通块,分别接到两个 \(P\) 点上。\(R\) 点的两个连通块在距离最长的两个 \(R\) 点间分割。

每对相邻的 \(P\) 点都可看作一个独立的小块计算代价并选择最优的方案,最后考虑 \(P,B\) 即可。

void solve() {
    int n;
    i64 ans{};
    std::cin >> n;
    bool R{}, B{}, P{};
    i64 lastR{}, lastB{};
    i64 cnt1{}, cnt2{}, cnt3{};
    for (int i = 0; i < n; i++) {
        i64 dis;
        char c;
        std::cin >> dis >> c;
        if (c == 'P' or c == 'R') {
            if (R) {
                ans += dis - lastR;//把相邻的R连边
                cnt1 = std::max(cnt1, dis - lastR);//维护P与R连通块相连的最小距离
            }
            R = true;
            lastR = dis;
        }
        if (c == 'P' or c == 'B') {
            if (B) {
                ans += dis - lastB;//把相邻的B连边
                cnt2 = std::max(cnt2, dis - lastB);//维护P与B连通块相连的最小距离
            }
            B = true;
            lastB = dis;
        }
        if (c == 'P') {
            if (P) {//把两个P连起来
                ans += std::min(0LL, dis - cnt1 - cnt2 - cnt3);
            }
            P = true;
            cnt1 = cnt2 = 0;
            cnt3 = dis;
        }
    }
    std::cout << ans << '\n';
}
posted @ 2024-03-28 16:21  加固文明幻景  阅读(5)  评论(0编辑  收藏  举报