还玩线段树吗?

前言&注明

我好像一万年没更新了?

化学!!!!!!!!!!!!!!!!!!!(我发个电)
在这里插入图片描述

珂朵莉树貌似挺有用的?(做做熟练泼粪题就知道了)

还是要有参考资料的:

先不启动,先说说珂朵莉树的起源

珂朵莉树原名老司机树(Old Driver Tree,ODT),由2017年一场CF比赛中提出的数据结构,因为题目背景主角是《末日时在做什么?有没有空?可以来拯救吗?》的主角珂朵莉,因此该数据结构被称为珂朵莉树。(引自珂朵莉树详解,我懒)

正片开始

啥是珂朵莉树?

通过 set 存放若干个用结构体表示的区间,每个区间的元素都是相同的一种较暴力的数据结构。

只要是涉及区间赋值操作的问题,就可以用珂朵莉树处理几乎任何有关区间信息的询问。(显然,前提是不会 TLE)

想要知道珂朵莉树怎么卡或者怎么会被卡,可见

所以,前置知识就来了

开始讲怎么写了

先扔出例题:Physical Education Lessons

定义

之前说过珂朵莉树要用结构体存储区间,所以定义就是写一个结构体。

struct Project_KDL_Tree{
	int l,r;
	mutable int val;
	inline bool operator<(const Project_KDL_Tree&BlastMike)const{
		return l<BlastMike.l;
	}//重载运算符,按左端点排序
	Project_KDL_Tree(int L,int R=-1,int Val=0):l(L),r(R),val(Val){}
};

BlastMike:孩子们,我回来了。
在这里插入图片描述

Split

该函数实现我们要从 set 中的所有区间中找到我们要找的 n o w now now 所在的区间,并拆成两个区间 [ l , n o w − 1 ] [l, now - 1] [l,now1] [ n o w , r ] [now, r] [now,r], 使 n o w now now 作为一个区间的开头,返回该区间的迭代器。

inline S_It Split(int now){
	S_It it=KDL_Tree.lower_bound(Project_KDL_Tree(now));//找到now的迭代器
	if(it!=KDL_Tree.end()&&it->l==now)
		return it;//若这个迭代器的l是的now,直接返回
	--it;//若不是则在前一个里面
	int l=it->l,r=it->r,val=it->val;
	KDL_Tree.erase(it);//删掉该区间
	KDL_Tree.insert(Project_KDL_Tree(l,now-1,val));//重新放入区间[l,now-1]和[now,r]
	return KDL_Tree.insert(Project_KDL_Tree(now,r,val)).first;//返回以now为开头的迭代器
}
Assign Val

若不断进行split函数,会导致 TLE,所以我们需要一个区间合并操作即针对区间赋值的一个操作。

inline void Assign_Val(int l,int r,int val){
	S_It itr=Split(r+1),itl=Split(l);//先找到r+1的迭代器位置,再找l的迭代器位置,具体为什么可以看set的性质
	S_It it;
	for(it=itl;it!=itr;++it)
		Answer-=it->val*(it->r-it->l+1);//计算贡献,具体因题目而异
	KDL_Tree.erase(itl,itr);//删掉这一段
	KDL_Tree.insert(Project_KDL_Tree(l,r,val));//重新插入所需区间
	Answer+=val*(r-l+1);//计算贡献,具体因题目而异
}

为什么要找 r + 1 r+1 r+1?因为 set 删除的时候传参是左闭右开的。

Code
#include<bits/stdc++.h>
using namespace std;
const int Maxn=2e5+5;
struct Project_KDL_Tree{
	int l,r;
	mutable int val;
	inline bool operator<(const Project_KDL_Tree&BlastMike)const{
		return l<BlastMike.l;
	}
	Project_KDL_Tree(int L,int R=-1,int Val=0):l(L),r(R),val(Val){}
};
#define S_It set<Project_KDL_Tree>::iterator
set<Project_KDL_Tree>KDL_Tree;
long long Answer;
inline S_It Split(int now){
	S_It it=KDL_Tree.lower_bound(Project_KDL_Tree(now));
	if(it!=KDL_Tree.end()&&it->l==now)
		return it;
	--it;
	int l=it->l,r=it->r,val=it->val;
	KDL_Tree.erase(it);
	KDL_Tree.insert(Project_KDL_Tree(l,now-1,val));
	return KDL_Tree.insert(Project_KDL_Tree(now,r,val)).first;
}
inline void Assign_Val(int l,int r,int val){
	S_It itr=Split(r+1),itl=Split(l);
	S_It it;
	for(it=itl;it!=itr;++it)
		Answer-=it->val*(it->r-it->l+1);
	KDL_Tree.erase(itl,itr);
	KDL_Tree.insert(Project_KDL_Tree(l,r,val));
	Answer+=val*(r-l+1);
}
int n,m;
signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	Answer=n;
	KDL_Tree.insert(Project_KDL_Tree(1,n,1));
	for(register int i=1,l,r,val;i<=m;++i){
		cin>>l>>r>>val;
		Assign_Val(l,r,val-1);
		cout<<Answer<<endl;
	}
	return 0;
}

然后你就会了

在这里插入图片描述

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部