还玩线段树吗?
前言&注明
我好像一万年没更新了?
化学!!!!!!!!!!!!!!!!!!!(我发个电)
珂朵莉树貌似挺有用的?(做做熟练泼粪题就知道了)
还是要有参考资料的:
- 珂朵莉树详解
(From:CSDN,@Suryxin.)
先不启动,先说说珂朵莉树的起源
珂朵莉树原名老司机树(Old Driver Tree,ODT),由2017年一场CF比赛中提出的数据结构,因为题目背景主角是《末日时在做什么?有没有空?可以来拯救吗?》的主角珂朵莉,因此该数据结构被称为珂朵莉树。(引自珂朵莉树详解,我懒)
正片开始
啥是珂朵莉树?
通过 set
存放若干个用结构体表示的区间,每个区间的元素都是相同的一种较暴力的数据结构。
只要是涉及区间赋值操作的问题,就可以用珂朵莉树处理几乎任何有关区间信息的询问。(显然,前提是不会 TLE)
想要知道珂朵莉树怎么卡或者怎么会被卡,可见此。
所以,前置知识就来了
- 关联式容器(set)(From:OI-Wiki)
- mutable(自己找资料,感觉可以不学)
开始讲怎么写了
先扔出例题: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,now−1] 和
[
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;
}