ChthollyTree 模板

这种暴力又可爱的数据结构真的相当讨喜。如果题目中有操作是推平一段区间的话,那么它多半就是一道 ODT 的题。如果题目还保证了数据随机的话,那么恭喜你,ODT 一定可以简单高效的解决这个问题;但如果出题人卡数据的话,ODT 一定会 T 的飞起。
掌握 ODT 的难度是相当小的,只要理解到 split 和 assign 就好了,这两种操作也是相当简单。其他附加的操作也是在这两种操作的基础上怎么暴力怎么来。

两道板子题

CF896C

传送们
万恶之源,也是珂朵莉树的名字来源。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL,int> PLI;
const int N=1e5+10;
const int mod=1e9+7;
int n,m;
LL seed,vmax,a[N];

LL qpow(LL x,LL k,LL mod){
	LL res=1;x%=mod;
	while(k){
		if(k&1) res=res*x%mod;
		x=x*x%mod;k>>=1;
	}
	return res;
}

struct ChthollyTree{
	#define setit set<Node>::iterator
	struct Node{
		int l,r;
		mutable LL x;
		friend bool operator < (Node a,Node b){return a.l<b.l;}
	};
	set<Node> st;
	void build(){for(int i=1;i<=n+1;i++) st.insert(Node{i,i,a[i]});}
	setit split(int pos){
		setit it=st.lower_bound(Node{pos});
		if(it!=st.end()&&it->l==pos) return it;
		it--;
		int l=it->l,r=it->r;LL x=it->x;
		st.erase(it);
		st.insert(Node{l,pos-1,x});
		return st.insert(Node{pos,r,x}).first;
	}
	void assign(int l,int r,LL x){
		setit itl=split(l),itr=split(r+1);
		st.erase(itl,itr);
		st.insert(Node{l,r,x});
	}
	void add(int l,int r,LL v){
		setit itl=split(l),itr=split(r+1);
		for(setit it=itl;it!=itr;it++) it->x+=v;
	}
	LL getnum(int l,int r,int k){
		setit itl=split(l),itr=split(r+1);
		vector<pair<LL,int> > vp;
		for(setit it=itl;it!=itr;it++) vp.push_back({it->x,it->r-it->l+1});
		sort(vp.begin(),vp.end());
		for(pair<LL,int> p:vp){
			k-=p.second;
			if(k<=0) return p.first;
		}
		return -1;
	}
	LL asksum(int l,int r,int k,int mod){
		setit itl=split(l),itr=split(r+1);
		LL ans=0;
		for(setit it=itl;it!=itr;it++)
			ans=( ans+qpow(it->x,k,mod)*(it->r-it->l+1) )%mod;
		return ans;
	}
}odt;

int rnd(){
	int ret=seed;
	seed=(seed*7+13)%mod;
	return ret;
}

int main(){
	cin>>n>>m>>seed>>vmax;
	for(int i=1;i<=n;i++) a[i]=rnd()%vmax+1;
	odt.build();
	while(m--){
		int opt=rnd()%4+1,l=rnd()%n+1,r=rnd()%n+1,x,y;
		if(l>r) swap(l,r);
		if(opt==3) x=rnd()%(r-l+1)+1;
		else x=rnd()%vmax+1;
		if(opt==4) y=rnd()%vmax+1;
		if(opt==1) odt.add(l,r,x);
		else if(opt==2) odt.assign(l,r,x);
		else if(opt==3) printf("%lld\n",odt.getnum(l,r,x));
		else if(opt==4) printf("%lld\n",odt.asksum(l,r,x,y));
	}
	return 0;
}

2019太原理工新生预赛L题

传送门
稍微简单一些的典型的珂朵莉树的应用

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <set>
#include <queue>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,m;
struct ChthollyTree{
	#define setit set<Node>::iterator
	struct Node{
		int l,r;
		mutable LL val;
		friend bool operator < (Node a,Node b){return a.l<b.l;}
	};
	set<Node> s;
	void build(){s.insert(Node{1,n,1});s.insert(Node{n+1,n+1,0});}
	setit split(int pos){
		setit it=s.lower_bound(Node{pos});
		if(it!=s.end()&&it->l==pos) return it;
		it--;
		int l=it->l,r=it->r,val=it->val;
		s.erase(it);
		s.insert(Node{l,pos-1,val});
		return s.insert(Node{pos,r,val}).first;
	}
	void assign(int l,int r,int val){
		setit itl=split(l),itr=split(r+1);
		s.erase(itl,itr);
		s.insert(Node{l,r,val});
	}
	void sumtor(int l,int r){
		setit itl=split(l),itr=split(r+1);
		LL sum=0;
		for(setit it=itl;it!=itr;it++) sum+=it->val*(it->r-it->l+1);
		assign(l,r-1,0);
		assign(r,r,sum);
	}
	int count(int l,int r){
		setit itl=split(l),itr=split(r+1);
		set<LL> st;
		int ans=0;
		for(setit it=itl;it!=itr;it++) if(it->val) st.insert(it->val);
		return st.size();
	}
}odt;

inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
	return x*f;
}

int main(){
	n=read();m=read();
	odt.build();
	for(int i=1,opt,l,r;i<=m;i++){
		opt=read(),l=read(),r=read();
		if(opt==1) odt.assign(l,r,1);
		else if(opt==2) odt.sumtor(l,r);
		else if(opt==3) printf("%d\n",odt.count(l,r));
	}
	return 0;
}
posted @ 2020-03-07 12:19  BakaCirno  阅读(117)  评论(0编辑  收藏  举报