珂朵莉树学习笔记

珂朵莉树学习笔记

其实是暴力学习笔记

珂朵莉树(Chtholly Tree),又名老司机树 ODT(Old Driver Tree)。起源自 CF896C

  • 原理:暴力
  • 用途:骗分,在有区间赋值的情况下,如果数据随机完全可以当正解打。

正文

前置知识: \(std::set\)

思路:用\(set\)或者\(map\)维护区间的信息,本人用的是\(set\)
左端点排序维护区间

CF896C为例

  1. 存结构体

    直接看代码吧

    \(code:\)

    struct node{
        int l,r;
        mutable ll val;
        bool operator < (const node &a) const {return l < a.l;}
        node(int L = 0,int R = 0,ll Val = 0): l(L),r(R),val(Val){}
    };
    set<node> s;
    #define sit set<node>::iterator
    

    其中 \(mutable\) 的用处是让我们可以直接在 \(set\) 中改变 \(val\) 的值

  2. 建树
    将需要的区间直接插入

    \(code:\)

    for(int i = 1;i <= n; ++i) T.insert(i,i,a[i]);
    
  3. 核心操作:

    1. \(split\)

      作用:将区间 \([l,r]\) 分裂成 \([l,pos)\)\([pos,r]\) 并返回 \([pos,r]\) 的迭代器

      \(code:\)

      inline sit split(int pos){
          sit it = s.lower_bound(node(pos));//先查找pos所在区间
          if(it != s.end() && it->l == pos) return it;//如果已经分好了直接返回即可
          -- it;//否则一定在上一个区间里
          int l = it->l,r = it->r,val = it->val;//存一下
          s.erase(it);//删除原区间
          s.insert(node(l,pos-1,val));//插入[l,pos)
          return s.insert(node(pos,r,val)).first;//插入[pos,r]并返回所在迭代器
          //因为insert的返回值类型为pair<set<typename>::iterator,bool>
          //typename为类型名
      }
      

      \(tips:\)珂朵莉树在进行求取区间左右端点操作时,必须先 \(split\) 右端点,再 \(split\) 左端点。若先 \(split\) 左端点,返回的迭代器可能在 \(split\) 右端点的时候失效,可能会导致 RE。

    2. \(assign\)

      高贵的区间推平,保证珂朵莉树复杂度的核心操作。

      \(code:\)

      inline void assign(int l,int r,ll val){
          sit it2 = split(r+1),it1 = split(l);//分裂区间
          s.erase(it1,it2);//将[it1,it2)从set中删去
          s.insert(node(l,r,val));//插入新的元素
      }
      
  4. 其他操作

    暴力即可

    区间加:

    inline void add(int l,int r,ll val){
        sit it2 = split(r+1),it1 = split(l);
        for(sit i = it1;i != it2; ++i) i->val += val;
    }
    

    区间第k大值:

    inline ll kth(int l,int r,int k){
        vector<pair<ll,int> > res;
        sit it2 = split(r+1),it1 = split(l);
        for(sit i = it1;i != it2; ++i) 
            res.emplace_back(make_pair(i->val,i->r - i->l +1));
        sort(res.begin(),res.end());
        for(auto i : res){
            k -= i.second;
            if(k <= 0) return i.first;
        }
        return 0;
    }
    

    区间幂次和:

    inline ll pow_sum(int l,int r,int x,int mod){
        sit it2 = split(r+1),it1 = split(l);
        ll res = 0;
        for(sit i = it1;i != it2; ++i)
            res = (res+1ll*(i->r - i->l +1)*power(i->val,x,mod)%mod)%mod;
        return res;
    }
    
  5. 复杂度分析(不会啊太难了看这个吧)

  6. 例题:

    骗分的暴力还有什么例题

update on 2024-7-29

今天模拟赛考了一道,但我没推出可以用这个东西维护,一直在想bitset了

挂一下原题链接,可以当一道ODT的例题

[JOISC 2023 Day3] Tourism

posted @ 2024-07-29 21:28  CuFeO4  阅读(33)  评论(0编辑  收藏  举报