题解 CF981G Magic multisets

update 2022.10.25 修改了下格式。

link

题意

给定 n 个可重集,初始为空。向可重集中加入一个正整数 x,如果 x 已经在该可重集出现,则可重集中每一个元素出现次数翻倍,否则直接加入。

q 次操作:

  • 下标在 [l,r] 的可重集加入数 x

  • 查询下标在 [l,r] 的可重集大小的和,对 998244353 取模。

数据范围:1n,q2×105,1xn

题解

这个题跟 P8416 处理方法有点像。

1xn,考虑每个 x 分开处理,用 01 表示可重集中有没有 x

这个问题相当于给定一个序列,加入数 x 相当于把序列区间推平成 1

这种区间推平问题很自然地想到颜色段均摊,即 ODT。

有个很经典的 trick 就是用 ODT 把区间推平转成区间加。

于是我们开 n 棵珂朵莉树维护集合是否有 x,再用一棵线段树维护集合大小的和即可。

这个题可以先把区间的集合大小乘 2,再把序列上为 0 的数除以 2 再加 1,所以用线段树维护区间乘区间加即可。

然后这个复杂度的正确性,因为颜色段均摊每次 split 最多增加 O(1) 个区间,每个区间删除 O(1) 次,因为最多 q 次操作,所以区间总数是 O(q) 级别的,所以总时间复杂度为 O(n+qlogn)

注意 split 要先分裂 r+1,不然可能会越界,经典错误了属于是。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inv2 = mod + 1 >> 1;
int n, q;
namespace segment_tree {
    #define ls x << 1
    #define rs x << 1 | 1
    int tagm[N << 2], taga[N << 2], sum[N << 2];
    void build(int x, int l, int r) {
        if(l == r) return;
        int mid = l + r >> 1;
        taga[x] = 0, tagm[x] = 1;
        build(ls, l, mid), build(rs, mid + 1, r);
    }
    void pushup(int x) { sum[x] = (sum[ls] + sum[rs]) % mod; }
    void pusha(int x, int l, int r, int v) {
        if(!v) return;
        taga[x] = (taga[x] + v) % mod;
        sum[x] = (sum[x] + 1ll * (r - l + 1) * v) % mod;
    }
    void pushm(int x, int v) {
        if(v == 1) return;
        tagm[x] = 1ll * tagm[x] * v % mod;
        taga[x] = 1ll * taga[x] * v % mod;
        sum[x] = 1ll * sum[x] * v % mod;
    }
    void pushdown(int x, int l, int r) {
        int mid = l + r >> 1;
        pushm(ls, tagm[x]), pushm(rs, tagm[x]);
        tagm[x] = 1;
        pusha(ls, l, mid, taga[x]), pusha(rs, mid + 1, r, taga[x]);
        taga[x] = 0;
    }
    void updatea(int x, int l, int r, int L, int R, int v) {
        if(L <= l && r <= R) { pusha(x, l, r, v); return; }
        pushdown(x, l, r);
        int mid = l + r >> 1;
        if(mid >= L) updatea(ls, l, mid, L, R, v);
        if(mid < R) updatea(rs, mid + 1, r, L, R, v);
        pushup(x);
    }
    void updatem(int x, int l, int r, int L, int R, int v) {
        if(L <= l && r <= R) { pushm(x, v); return; }
        pushdown(x, l, r);
        int mid = l + r >> 1;
        if(mid >= L) updatem(ls, l, mid, L, R, v);
        if(mid < R) updatem(rs, mid + 1, r, L, R, v);
        pushup(x);
    }
    int query(int x, int l, int r, int L, int R) {
        if(L <= l && r <= R) return sum[x];
        pushdown(x, l, r);
        int mid = l + r >> 1, res = 0;
        if(mid >= L) res = (res + query(ls, l, mid, L, R)) % mod;
        if(mid < R) res = (res + query(rs, mid + 1, r, L, R)) % mod;
        return res;
    }
} using namespace segment_tree;
struct old_driver_tree {
    struct odt {
        int l, r;
        mutable int v;
        odt(int L = 0, int R = 0, int V = 0) { l = L, r = R, v = V; }
        friend bool operator < (const odt &qwq, const odt &awa) {
            return qwq.l < awa.l;
        }
    };
    set<odt> s;
    void init() { s.insert(odt(1, n)); }
    #define IT set<odt>::iterator
    #define pii pair<int, int>
    IT split(int x) {
        IT it = s.lower_bound(odt(x, 0, 0));
        if(it != s.end() && it -> l == x) return it;
        --it;
        int L = it -> l, R = it -> r, V = it -> v;
        s.erase(it);
        s.insert(odt(L, x - 1, V));
        return s.insert(odt(x, R, V)).first;
    }
    void assign(int l, int r) {
        updatem(1, 1, n, l, r, 2);
        IT itr = split(r + 1), itl = split(l);
        for(IT it = itl; it != itr; ++it)
            if(!(it -> v))
           updatem(1, 1, n, it -> l, it -> r, inv2),
                updatea(1, 1, n, it -> l, it -> r, 1);
        s.erase(itl, itr);
        s.insert(odt(l, r, 1));
    }
} t[N];
int main() {
    scanf("%d%d", &n, &q);
    build(1, 1, n);
    for(int i = 1; i <= n; i++)
        t[i].init();
    while(q--) {
        int opt, l, r, x;
        scanf("%d%d%d", &opt, &l, &r);
        if(opt == 1) {
            scanf("%d", &x);
            t[x].assign(l, r); 
        }

        else printf("%d\n", query(1, 1, n, l, r));
    }
    return 0;
}
posted @   Terac  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示