ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

珂朵莉树

2022-08-05 12:36:22  阅读:172  来源: 互联网

标签:Node ll pos 朵莉树 int64 split 区间


珂朵莉树(\(ODT\))

0x01 珂朵莉树可以解决什么问题

对于涉及区间推平操作的问题,就是把\([l, r]\)区间内的所有数字变成相同的一个数。如果数据是随机的就可以使用珂朵莉树。

0x02 珂朵莉树的基本原理

比如一开始把一个区间分成这些部分,那么我们就可以用一个结构体将相邻的数字存起来,分别记录他们的始末点和这一段区间内的数字是多少。

struct Node {
    ll l, r;//l和r表示这一段的起点和终点
    mutable ll v;//v表示这一段上所有元素相同的值是多少

    Node(ll l, ll r = 0, ll v = 0) : l(l), r(r), v(v) {}

    bool operator<(const Node &a) const {
        return l < a.l;//规定按照每段的左端点排序
    }
};

关键字\(mutable\) 的意思是即使是一个常数,也允许修改\(v\)的值
将结构体存储在\(set\)里面按照左端点进行排序,就变成了下图的样子。

0x03 核心函数

\(split 分裂\)

因为我们要对一个区间内的所有数字进行操作,那么我们只需要将这个区间从原来的区间中取出来修改好了之后再放回去,就可以高效的实现区间推平操作了。
我们按照\(pos\)这个位置来进行分裂,将\([l, r]\)区间分裂成\([l, pos - 1], [pos, r]\)两个区间。如果\(pos\)本身就是一个区间的开头,那么就不需要去分割了直接返回这个区间就可以了。

set<Node>::iterator split(int pos) {
    set<Node>::iterator it = s.lower_bound(Node(pos));
    if (it != s.end() && it->l == pos) {
        return it;
    }
    it--;
    if (it->r < pos) return s.end();
    ll l = it->l;
    ll r = it->r;
    ll v = it->v;
    s.erase(it);
    s.insert(Node(l, pos - 1, v));
    //insert函数返回pair,其中的first是新插入结点的迭代器
    return s.insert(Node(pos, r, v)).first;
}

将区间取出来之后,我们要确定我们找到的这个\(it\)是刚好是在以\(pos\)开头的这个区间,还是\(pos\)稍微大了一点点或者\(pos\)过大超出了最后一个区间。如果是第一种情况的话就可以直接返回这个区间,如果是后两种情况的话,可以先将\(it\)这个迭代器向前退一个单位,然后这个时候看看区间的右端点和\(pos\)的大小关系,如果小于\(pos\)的话,那么就说明\(pos\)太大了,直接返回\(s.end()\),否则的话就将区间一分为二再重新插入回去。

\(assign 推平\)

如图,我们将这个区间取出之后,将\(Node结构体\)重新插入回去,就实现了。

void assign(ll l, ll r, ll x) {
    set<Node>::iterator itr = split(r + 1), itl = split(l);
    s.erase(itl, itr);
    s.insert(Node(l, r, x));
}

当然,这个时候一定要先将区间的右端点取出来再将左端点取出来,否则可能会出现\(RE\)的情况。

\(add\)修改操作

就是将区间分裂之后,循环遍历一遍区间,将所有的数都加上\(x\)就好了。

void add(ll l, ll r, ll x) {
    set<Node>::iterator itr = split(r + 1), itl = split(l);
    for (set<Node>::iterator it = itl; it != itr; ++it) {
        it->v += x;
    }
}

0x04整体的模板

struct ODT {
	struct Node {
		int64_t l, r;
		mutable int64_t v;

		Node (int64_t l, int64_t r = 0, int64_t v = 0) : l(l), r(r), v(v) {}

		bool operator < (const Node& lhs) const {
			return l < lhs.l;
		}
	};
	std::set<Node> s;

	std::set<Node>::iterator split(int pos) { // 分裂区间
		std::set<Node>::iterator it = s.lower_bound(Node(pos));
		if (it -> l == pos && it != s.end()) 
			return it;
		-- it;
		if (it -> r < pos) 
			return s.end();

		int64_t l = it -> l, r = it -> r, v = it -> v;
		s.erase(it);
		s.insert(Node(l, pos - 1, v));
		return s.insert(Node(pos, r, v)).first;
	}

	void assign(int l, int r, int64_t x) {    //区间推平
		std::set<Node>::iterator itr = split(r + 1), itl = split(l);
		s.erase(itl, itr);
		s.insert(Node(l, r, x));
	}

	void add(int64_t l, int64_t r, int64_t x) {    //区间加法
		std::set<Node>::iterator itr = split(r + 1), itl = split(l);
		for (auto it = itl; it != itr; it ++ ) 
			it -> v += x;
	}

	struct Rank {
		int64_t val, cnt;
		bool operator < (const Rank& lhs) const {
			return val < lhs.val;
		}

		Rank(int64_t val, int64_t cnt) : val(val), cnt(cnt) {}
	};

	int rank(int64_t l, int64_t r, int64_t x) {    //查询区间排名为x的数是多少
		std::set<Node>::iterator itr = split(r + 1), itl = split(l);
		std::vector<Rank> vec;
		for (auto it = itl; it != itr; ++ it )
			vec.push_back(Rank(it -> v, it -> r - it -> l + 1));
		std::sort(all(vec));

		int64_t ans = -1;
		for (auto p : vec) { 
			if (p.cnt < x) 
				x -= p.cnt;
			else {
				ans = p.val;
				break;
			}
		}
		return ans;
	}
	
	int64_t query(int64_t l, int64_t r) { // 区间和
		auto itr = split(r + 1), itl = split(l);
		int64_t ans = 0;
		for (auto it = itl; it != itr; ++ it) 
			ans += (it -> v) * (it -> r - it -> l + 1); 
		return ans;
	}
};

标签:Node,ll,pos,朵莉树,int64,split,区间
来源: https://www.cnblogs.com/Haven-/p/16553875.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有