• 好Van的珂朵莉树


    珂朵莉树

    珂朵莉树的主要操作是区间覆盖,即将区间([l,r])全部染色为(c)

    EXAMPLE

    EXAMPLE 1

    给出一个长度为(n)的序列,一共(q)次询问,每次询问给出(m)个区间,求这些区间并集的权值和。

    (n leq 10^5,sum m leq 10^5)

    SOLUTION 1

    显然能用珂朵莉树做

    珂朵莉树是一种基于std::set的暴力数据结构。

    这个set维护若干区间,这些区间没有交集,且按左端点从小到大有序。

    struct node {
    	int l, r, c;
    	int operator<(const node& a) const { return l < a.l; }
    };
    set<node> S;
    typedef set<node>::iterator IT;
    

    珂朵莉树的基本操作有两个:

    • split(pos)

    这个操作把包含了(pos)位置的区间分成两段,分别为([l,pos-1])([pos,r]),且返回([pos,r])的指针。

    写法也很简单:

    IT split(int po) {
    	IT it = S.lower_bound((node){po, -1, 0});
    	if (it != S.end() && it->l == po) return it;
    	--it;
    	int l = it->l, r = it->r, c = it->c;
    	S.erase(it);
    	S.insert((node){l, po - 1, c});
    	return S.insert((node){po, r, c}).first;
    }
    
    • assign(l, r, c)

    这个操作将被([l,r])包含的区间全部删除,然后用颜色(c)覆盖区间([l,r])

    代码:

    void cover(int l, int r, int c) {
    	IT itr = split(r + 1), itl = split(l);
    	S.erase(itl, itr); //意思是删除[itl, itr),注意左闭右开
    	S.insert((node){l, r, c});
    	reset(c, sum[r] - sum[l - 1]);
    }
    

    那么,对于上面那题,怎么用珂朵莉树解决?

    对于每个询问,我们直接cover(l, r),由于操作的是整个区间,答案的增减可以直接(O(1))得到,代码只需要在上面的写法上变一下即可:

    void cover(int l, int r, int c) {
    	IT itr = split(r + 1), itl = split(l), it = itl;
    	for (; it != itr; ++it) {
        	//这里可以直接根据it的左右端点增减答案
    	}
    	S.erase(itl, itr);
    	S.insert((node){l, r, c});
    	reset(c, sum[r] - sum[l - 1]);
    }
    

    EXAMPLE 2

    给出一个(n)个节点的树以及(m)条树上路径,共(q)个询问,每次询问([l_i,r_i])这些路径的并的权值。

    SOLUTION 2

    先离线询问,把询问([l,r])挂在(r)。从左往右扫时,我们把第(i)条路径上的每个点染色为(i),然后遍历挂在(i)上的询问,设其左端点是(l),那么颜色(>=l)的点都有贡献。

    树链剖分后路径染色转化为区间问题,为了统计(c>=l)的贡献和,我们再使用树状数组维护,用珂朵莉树解决染色问题,这题就搞定了。

    复杂度

    如果被覆盖的区间毫无贡献,那么可以证明珂朵莉树复杂度是(O(nlogn))的。

    因为每次assign最多增加(3)个区间,每次增加是(O(logn))的,删除区间是均摊(O(logn))的,所以复杂度是(O(nlogn))的。


    又扩展技能树啦~

  • 相关阅读:
    gtest ASSERT_TRUE和EXPECT_TRUE
    linux中文乱码
    [转]linux实时查看更新日志命令
    显示log里的ansi codecs颜色字符
    批量注释LOG
    Winrm远程命令/端口复用后门复现运用
    SSM整合Redis中的小记录
    从《MySQL蜜罐攻击反制微信ID》中的启发与复现
    CSharp插件编写-GetPwd密码获取器
    从pipePotato中学习Windows Access Token令牌模拟
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11566816.html
Copyright © 2020-2023  润新知