还玩线段树吗?
前言&注明
我好像一万年没更新了?
化学!!!!!!!!!!!!!!!!!!!(我发个电)
珂朵莉树貌似挺有用的?(做做熟练泼粪题就知道了)
还是要有参考资料的:
- 珂朵莉树详解
(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;
}