牛客 NC21125 践踏

文章讨论了一种利用树状数组和差分数组技术,针对区间加单点查询的问题进行高效处理的方法。当k不为0时,通过取模操作和区间划分来优化查询效率;当k为0时,采用离散化处理大数据。
摘要由CSDN通过智能技术生成

分析

  • x + k ⋅ t    ,    t ∈ Z x+k\cdot t\;,\;t\in Z x+kt,tZ 对于 y ∈ [ 0    ,    k − 1 ] y\in[0\;,\;k-1] y[0,k1] 存在 ∀ x = y + k ⋅ t \forall x=y+k\cdot t x=y+kt ,所以对于一个区间 [ l    ,    r ] [l\;,\;r] [l,r] 只用记录该区间所包含的对应的 y ∈ [ 0    ,    k − 1 ] y\in[0\;,\;k-1] y[0,k1]
  • 如果区间长度 ≥ k \ge k k ,一定包含了整个 [ 0    ,    k − 1 ] [0\;,\;k-1] [0,k1] ,如果区间长度 ≤ k \leq k k ,对 l , r l,r l,r 取模后会有两种情况。
    • l ≤ r l\leq r lr ,正常操作。
    • l > r l>r l>r ,分为 [ 0    ,    r ]    ,    [ l    ,    k ] [0\;,\;r]\;,\;[l\;,\;k] [0,r],[l,k] 操作。
  • 查询只需要查询 x % k x\%k x%k 即可。
  • 那么就是一个区间加,单点查询的操作,用树状数组维护一个差分数组即可。
  • 对于 k = 0 k=0 k=0 ,就是正常的不用取模的操作,但是数据比较大,离散化即可。
  • 有个小细节,一般树状数组是以 0 0 0 退出循环,但是 x , l , r x,l,r x,l,r 的取值可以为 0 0 0 ,所以向后退一位即可。见代码。

Think Twice, Code Once

vector<int> tr(N);
signed main() {
    auto add = [] (int x, int v, int n) {
        for (int i = x + 1; i <= n; i += lowbit(i)) tr[i] += v;
    };
    auto query = [&] (int r) {
        int ans = 0;
        for (int i = r + 1; i; i -= lowbit(i)) ans += tr[i];
        return ans;
    };
    int T = 1;
//    T = read();
    while (T--) {
        int n = read(), k = read();
        if (!n) return puts("fafa"), 0;
        if (!k) {
            vector<int> vec;
            vector<tuple<int, int, int>> Opt;
            while (n--) {
                int op = read();
                if (op ^ 3) {
                    int l = read(), r = read();
                    Opt.push_back({op, l, r});
                    vec.push_back(l), vec.push_back(r);
                }
                else {
                    int x = read();
                    Opt.push_back({op, x, 0});
                    vec.push_back(x);
                }
            }
            sort(vec.begin(), vec.end());
            vec.erase(unique(vec.begin(), vec.end()), vec.end());
            for (auto [op, l, r]: Opt) {
                if (op ^ 3) {
                    int pos1 = lower_bound(vec.begin(), vec.end(), l) - vec.begin();
                    int pos2 = lower_bound(vec.begin(), vec.end(), r) - vec.begin();
                    if (op ^ 1) add(pos1, -1, vec.size()), add(pos2 + 1, 1, vec.size());
                    else add(pos1, 1, vec.size()), add(pos2 + 1, -1, vec.size());
                }
                else {
                    int pos = lower_bound(vec.begin(), vec.end(), l) - vec.begin();
                    writeln(query(pos));
                }
            }
        }
        else {
            while (n--) {
                int op = read();
                if (op ^ 3) {
                    int l = read(), r = read();
                    if (r - l + 1 >= k) {
                        if (op ^ 1) add(0, -1, k);
                        else add(0, 1, k);
                    }
                    else {
                        l %= k, r %= k;
                        if (l <= r) {
                            if (op ^ 1) add(l, -1, k), add(r + 1, 1, k);
                            else add(l, 1, k), add(r + 1, -1, k);
                        }
                        else {
                            if (op ^ 1) {
                                add(0, -1, k), add(r + 1, 1, k);
                                add(l, -1, k);
                            }
                            else {
                                add(0, 1, k), add(r + 1, -1, k);
                                add(l, 1, k);
                            }
                        }
                    }
                }
                else {
                    int x = read() % k;
                    writeln(query(x));
                }
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Heredy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值