华中农业大学2023年十二届程序设计竞赛(同步赛)G. 简单的开关(思维 括号匹配+差分+前缀和 set/珂朵莉树)

题目

小夏有一台神奇的计算机,这个计算机有n个开关,初始状态均为关,

接下来,小夏想对这台计算机进行m次操作,每次操作给出三个数l,r,k,

表示将第l个(含l)到第r个(含r)开关的状态设置为开或者关(k=0表示关、k=1表示开)

m次操作完之后,小夏想知道这台计算机中,

有多少个区间满足开启状态开关的个数等于关闭状态开关的个数

思路来源

heltion代码

题解

先离散化,配合set乱搞(可用时间逆序+set二分,或用珂朵莉树),将序列分成若干个0、1区间

01区间开关个数相等,

可以把1看成(,遇到(就+1,把0看成是),遇到)就-1,

从而转化为前缀值域相等的问题,

每一段区间[l,r),记录区间开始的前缀和值和区间终止时的前缀和值,

再按照值域差分,每一个区间对应区间加1,最后统计每段值域的出现次数

对于某一段值域[l,r),其中的每个值的出现次数都相等,

设出现次数为x,则C(x,2)就能找到两个合法端点L-1和R,确定题目所求区间[L,R]

partial_sum(sum.begin(), sum.end(), sum.begin()); // 对sum数组做前缀和,并保存到sum内

代码1(时间逆序+差分+前缀和)

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<int> l(m), r(m), k(m);
    set<int> s;
    s.insert(1);
    s.insert(n + 1);
    for (int i = 0; i < m; i += 1) {
        cin >> l[i] >> r[i] >> k[i];
        s.insert(l[i]);
        s.insert(r[i] + 1);
    }
    set<pair<int, int>> sp;
    for (auto it = s.begin(); next(it) != s.end(); it = next(it)) {
        sp.emplace(*it, *next(it));
    }
    set<tuple<int, int, int>> st;
    for (int i = m - 1; i >= 0; i -= 1) {
        for (auto it = sp.lower_bound(make_pair(l[i], 0));
             it != sp.end() and it->first <= r[i]; it = sp.erase(it)) {
            st.emplace(it->first, it->second, k[i]);
        }
    }
    for (auto [l, r] : sp) {
        st.emplace(l, r, 0);
    }
    vector<pair<int, int>> vp;
    vp.emplace_back(0, 1);
    int cur = 0;
    for (auto [l, r, k] : st) {
        if (k) {
            vp.emplace_back(cur + 1, cur + r - l + 1);
            cur += r - l;
        } else {
            vp.emplace_back(cur + l - r, cur);
            cur += l - r;
        }
    }
    vector<int> x;
    for (auto [l, r] : vp) {
        x.emplace_back(l);
        x.emplace_back(r);
    }
    sort(x.begin(), x.end());
    x.erase(unique(x.begin(), x.end()), x.end());
    vector<int> sum(x.size());
    for (auto [l, r] : vp) {
        sum[lower_bound(x.begin(), x.end(), l) - x.begin()] += 1;
        sum[lower_bound(x.begin(), x.end(), r) - x.begin()] -= 1;
    }
    partial_sum(sum.begin(), sum.end(), sum.begin());
    LL ans = 0;
    for (int i = 0; i + 1 < x.size(); i += 1) {
        ans += (LL)sum[i] * (sum[i] - 1) / 2 * (x[i + 1] - x[i]);
    }
    cout << ans;
}

代码2(珂朵莉树+差分+前缀和)

后面的逻辑相同,前面的区间覆盖用珂朵莉树维护

1. split将包含x的区间[l,r)拆成两个区间[l,x)和[x,r)

2. assign将[l,r]内的每个区间删掉,合并为同一个[l,r],赋值为k

#include "bits/stdc++.h"

using namespace std;
using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m;

    map<int, int> mp;
    mp[1] = 0;
    mp[n + 1] = 0;
    
    auto split = [&](int x) {
        auto it = prev(mp.upper_bound(x));
        mp[x] = it->second;
    };

    auto assign = [&](int l, int r, int k) {
        split(l);
        split(r + 1);
        auto it = mp.find(l);
        while (it->first != r + 1) {
            it = mp.erase(it);
        }
        mp[l] = k;
    };

    for (int i = 0; i < m; i++) {
        int l, r, k;
        cin >> l >> r >> k;
        assign(l, r, k);
    }
    
    vector<pair<int, int>> a;
    a.push_back({0, 1});
    int cur = 0;
    for (auto it = mp.begin(); it != prev(mp.end()); it++) {
        auto &[u, v] = *it;
        int l = u;
        int r = next(it)->first;
        int k = v;
        if (k) {
            a.push_back({cur + 1, cur + (r - l) + 1});
            cur += (r - l);
        } else {
            a.push_back({cur - (r - l), cur});
            cur -= (r - l);
        }
    }    
    
    vector<int> b;
    for (auto &[l, r] : a) {
        b.push_back(l);
        b.push_back(r);
    }
    sort(b.begin(), b.end());
    b.erase(unique(b.begin(), b.end()), b.end());
    
    int N = b.size();
    vector<i64> pre(N);
    for (auto &[l, r] : a) {
        pre[lower_bound(b.begin(), b.end(), l) - b.begin()]++;
        pre[lower_bound(b.begin(), b.end(), r) - b.begin()]--;
    }
    
    for (int i = 0; i + 1 < N; i++) {
        pre[i + 1] += pre[i];
    }
    
    i64 ans = 0;
    for (int i = 0; i + 1 < N; i++) {
        ans += pre[i] * (pre[i] - 1) / 2 * (b[i + 1] - b[i]);
    }
    cout << ans << '\n';

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值