我重生了,学会了珂朵莉树

还玩线段树吗?

前言&注明

我好像一万年没更新了?

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

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

还是要有参考资料的:

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

珂朵莉树原名老司机树(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;
}

然后你就会了

在这里插入图片描述

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一份女频重生文大纲,供您参考: 一、角色介绍 1. 女主角:原本是一名普通职场白领,因为某些原因不幸身亡,后来穿越到了另一个世界,重生为一个小家碧玉。 2. 男主角:身份高贵,是贵族公子,但内心孤独寂寞。与女主角的相遇成为了两人人生的转折点。 二、女主角的重生 1. 女主角重生为小家碧玉,开始了全新的人生。 2. 女主角意外获得前世记忆,开始重新规划自己的人生,决心不再让命运左右自己的命运。 3. 女主角遇到了男主角,两人开始产生微妙的情感变化。 三、女主角的成长 1. 女主角开始学习各种家教和礼仪,开始为自己的未来打算。 2. 女主角利用前世的知识和经验,开始寻找机会,逐渐展露出自己的才华和智慧。 3. 女主角在不断成长中,也逐渐发现了自己内心的情感,同时也逐渐发现了男主角的秘密。 四、男女主角的感情变化 1. 男主角开始被女主角的勇气和聪明才智所吸引,与女主角越来越亲近。 2. 女主角也开始对男主角产生了感情,但由于身份差异,两人之间的感情一度受到了阻碍。 3. 在各种磨难和考验之后,男女主角最终化解了彼此之间的矛盾,最终走到了一起。 五、故事结局 1. 男女主角在经历了各种波折后终于在一起,但是他们的未来之路仍然充满了挑战和不确定性。 2. 故事最终以一个带有悬念和期待的结局结束,让读者们自行想象二人之后的未来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值