CF 771 E. Colorful Operations(珂朵莉树+线段树)


传送门
这个题我第一眼望过去还以为CF终于出了一个数据结构题,然而实际上这就是一个数据结构题

题意:

维护一个长度为n的序列,序列的每一个元素大小\(a[i]\)初始为\(0\),颜色初始为\(1\).
有三个操作执行\(q\)
1.把\([l,r]\)涂成颜色\(c\)
2.把所有颜色为\(c\)的元素加上\(x\)
3.输出\(a[i]\)
其中\(n、q \leq 10^{6}\)
数据随机

题解:

借助此题学习一下珂朵莉树。
它不是一个数据结构而是一种方法

其大致的思想就是把一个序列的元素用一个三元组\(node(l,r,val)\)表示,然后对于操作暴力解决。储存这个三元组一般时候set

因为在数据随机的情况下这样的一个序列平均是只用\(log(n)\)个三元组表示的状态(不会证明)

对于这道题来说:
记录一个\(lazy\)标记,\(lazy[c]\)表示对颜色\(c\)的元素操作的总和。

对于操作1,利用珂朵莉树的思想暴力修改(时间复杂度\(O(logn)\))。同时对于所有的\(a[i]\)加上\(lazy[col\_pre]-lazy[col\_now]\)由于在使用珂朵莉树的技巧之后每一个元素有同一个颜色,所以这一步实际上就是区间加,使用线段树实现(时间复杂度为\(O(log^2n)\))。

对于操作2,直接修改\(lazy[c]\)

对于操作3,\(query(i)+lazy[col(i)]\),前半部分是线段树的查询操作,\(col(i)\)可以对\(set\)\(lower_bound\)\(log\)的时间得到。

珂朵莉树的具体实现

\(set\)的一些知识
1.\(set.end()\)并不是\(set\)中最后一个元素的迭代器,其数值上等于\(set.size()\)
2.\(set.erase(a,b)\)可以删除迭代器\(a\)\(b\)之间的所有元素。

\(set\)的定义

struct node{
	int l,r;
	mutable int col;
	node(int L,int R,int COL){
		l=L,r=R,col=COL;
	} 
};
bool operator <(node a,node b){
	return a.l st;

\(split\)

set::iterator split(int pos){
	auto it=st.lower_bound(node(pos,pos,pos));
	if(it!=st.end()&&it->l==pos)return it;
	it--;
	node tmp=*it;
	st.erase(it);
	st.insert(node(tmp.l,pos-1,tmp.col));
	return st.insert(node(pos,tmp.r,tmp.col)).first;
}

\(split(pos)\)的作用是将\(pos\)所在的\(node\)分成一个以\(pos\)为开始的\(node\)和另一个\(node\)并会返回一个迭代器(一个指针),指向\(pos\)所在的\(node\)

执行\(split(r+1)\)\(split(l)\)就可以将区间\([l,r]\)左右边界划出。

\(assign\)

void assign(int l,int r,int col){
	auto itr=split(r+1),itl=split(l); 
	for(auto i=itl;i!=itr;i++)
		add(1,n,i->l,i->r,1,Lazy[i->col]-Lazy[col]);
	st.erase(itl,itr);
	st.insert(node(l,r,col));
}

\([l,r]\)合并成一个\(node\)并且完成维护
注意这里一定要先\(split(r+1)\)\(split(l)\),否则\(itl\)会丢失。

\(find\_col\)

int get_col(int x){
	auto it=st.lower_bound(node(x,x,x));
	if(it!=st.end()&&it->l==x)return it->col;
	it--;
	return it->col;
}

完整代码

#include
#include
#include
#include
#include
#include
using namespace std;
#define mid (L+R>>1)
#define ls (now<<1)
#define rs (now<<1|1)
const int N=1010000;
int read(){
	int sum=0,f=1;;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		sum=sum*10+ch-'0';
		ch=getchar();
	}
	return sum*f;
}
long long sum[N*8],lazy[N*8],Lazy[N];
int n;
char s[10];
void update(int now){
	sum[now]=sum[ls]+sum[rs];
}
void pushdown(int L,int R,int now){
	if(lazy[now]==0)return;
	lazy[ls]+=lazy[now];
	lazy[rs]+=lazy[now];
	sum[ls]+=(mid-L+1)*lazy[now];
	sum[rs]+=(R-mid)*lazy[now];
	lazy[now]=0;
}
void add(int L,int R,int l,int r,int now,long long w){
	pushdown(L,R,now);
	if(L==l&&r==R){
		lazy[now]+=w;
		sum[now]+=(r-l+1)*w;
		return ;
	}
	if(l>mid)add(mid+1,R,l,r,rs,w);
	else if(r<=mid)add(L,mid,l,r,ls,w);
	else add(L,mid,l,mid,ls,w),add(mid+1,R,mid+1,r,rs,w);
	update(now); 
}
long long query(int L,int R,int x,int now){
	pushdown(L,R,now);
	if(L==R)return sum[now];
	if(x>mid)return query(mid+1,R,x,rs);
	else return query(L,mid,x,ls);
}
struct node{
	int l,r;
	mutable int col;
	node(int L,int R,int COL){
		l=L,r=R,col=COL;
	} 
};
bool operator <(node a,node b){
	return a.l st;
set::iterator split(int pos){
	auto it=st.lower_bound(node(pos,pos,pos));
	if(it!=st.end()&&it->l==pos)return it;
	it--;
	node tmp=*it;
	st.erase(it);
	st.insert(node(tmp.l,pos-1,tmp.col));
	return st.insert(node(pos,tmp.r,tmp.col)).first;
}
void assign(int l,int r,int col){
	auto itr=split(r+1),itl=split(l); 
	for(auto i=itl;i!=itr;i++)
		add(1,n,i->l,i->r,1,Lazy[i->col]-Lazy[col]);
	st.erase(itl,itr);
	st.insert(node(l,r,col));
}
int get_col(int x){
	auto it=st.lower_bound(node(x,x,x));
	if(it!=st.end()&&it->l==x)return it->col;
	it--;
	return it->col;
}
int main(){
	int q;
	n=read(),q=read();
	st.insert(node(1,n,1));
	while(q--){
		scanf("%s",s+1);
		getchar();
		if(s[1]=='C'){
			int l=read(),r=read(),c=read();
			assign(l,r,c);
		}
		if(s[1]=='A'){
			int c=read(),w=read();
			Lazy[c]+=w;
		}
		if(s[1]=='Q'){
			int x=read();
			printf("%lld\n",query(1,n,x,1)+Lazy[get_col(x)]);
		}
	}
	return 0;
}