[luogu2042] [NOI2005]维护数列

前言

写写比较麻烦的这题

题目相关

题目大意

写一个大数据结构

数据范围

20000
题目链接

前置

先过模板题,比如会个非旋treap,写一下,通过[luogu3369][模板]普通平衡树
possible version

#include
#include
#include
#include
#include
#include
#define rg register
namespace fast_IO
{
     
	const int IN_LEN=1000000,OUT_LEN=1000000;
	char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
	inline char getchar_(){
     return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
	inline void putchar_(const char x){
     if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
	inline void flush(){
     fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
typedef long long ll;
template<typename T>inline T min(const T x,const T y){
     return x<y?x:y;}
template<typename T>inline T max(const T x,const T y){
     return x>y?x:y;}
template<typename T>inline T abs(const T x){
     return x>0?x:-x;}
template<typename T>inline T gcd(const T a,const T b){
     if(!b)return a;return gcd(b,a%b);}
template<typename T>inline void swap(T&a,T&b){
     const T c=a;a=b;b=c;}
template<typename T>inline void read(T&x)
{
     
	char cu=getchar();bool fla=0;x=0;
	while(!isdigit(cu)){
     if(cu=='-')fla=1;cu=getchar();}
	while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
	if(fla)x=-x;
}
template<typename T>inline void printe(const T x)
{
     
	if(x>=10)printe(x/10);
	putchar(x%10+'0'); 
}
template<typename T>inline void print(const T x)
{
     
	if(x<0)putchar('-'),printe(-x);
	else printe(x); 
}
const int maxn=500001;
struct Point
{
     
	int x,key,lson,rson,size;
}Q[maxn];int usd,root;
int newnode(const int v)
{
     
	usd++;
	Q[usd]=(Point){
     v,rand(),0,0,1};
	return usd;
}
void update(const int x)
{
     
	Q[x].size=Q[Q[x].lson].size+Q[Q[x].rson].size+1;
}
std::pair<int,int> split(const int x,const int v)
{
     
	std::pair<int,int>res(0,0);
	if(!x)return res;
	if(Q[x].x<=v)
	{
     
		res=split(Q[x].rson,v);
		Q[x].rson=res.first;
		res.first=x;
	}
	else
	{
     
		res=split(Q[x].lson,v);
		Q[x].lson=res.second;
		res.second=x;
	}
	update(x);
	return res;
}
int merge(int x,int y)
{
     
	if(!x)return y;
	if(!y)return x;
	if(Q[x].key<Q[y].key)
	{
     
		Q[x].rson=merge(Q[x].rson,y);
		update(x);
		return x;
	}
	else
	{
     
		Q[y].lson=merge(x,Q[y].lson);
		update(y);
		return y;
	}
}
void insert(const int x)
{
     
	std::pair<int,int> R=split(root,x);
	root=merge(merge(R.first,newnode(x)),R.second);
}
void del(const int x)
{
     
	std::pair<int,int> R=split(root,x),T=split(R.first,x-1);
	if(T.second==0)return;
	root=merge(merge(T.first,merge(Q[T.second].lson,Q[T.second].rson)),R.second);
}
int rerank(int v)
{
     
	int x=root,ans=0;
	while(x)
	{
     
		if(Q[x].x<v)ans+=Q[Q[x].lson].size+1,x=Q[x].rson;
		else x=Q[x].lson;
	}
	return ans+1;
}
int atrank(int rk)
{
     
	int x=root;
	while(1)
	{
     
		const int all=Q[Q[x].lson].size+1;
		if(all==rk)return Q[x].x;
		if(all<rk)x=Q[x].rson,rk-=all;
		else x=Q[x].lson;
	}
}
int prec(int v)
{
     
	int x=root,ans=0;
	while(x)
	{
     
		if(Q[x].x<v)ans=Q[x].x,x=Q[x].rson;
		else x=Q[x].lson;
	}
	return ans;
}
int succ(int v)
{
     
	int x=root,ans=0;
	while(x)
	{
     
		if(Q[x].x>v)ans=Q[x].x,x=Q[x].lson;
		else x=Q[x].rson;
	}
	return ans;
}
int n;
int main()
{
     
	read(n),srand(time(0));
	for(rg int i=1;i<=n;i++)
	{
     
		int opt,x;read(opt),read(x);
		if(opt==1)insert(x);
		else if(opt==2)del(x);
		else if(opt==3)print(rerank(x)),putchar('\n');
		else if(opt==4)print(atrank(x)),putchar('\n');
		else if(opt==5)print(prec(x)),putchar('\n');
		else print(succ(x)),putchar('\n');
	}
	return flush(),0;	
}

题解

我们发现这里是要维护序列,那么我们需要把split魔改一下,改成“把第1-x个数放到左子树,其余放到右子树”

接下来我们考虑操作
首先我们发现初始在treap中的那些数等价于一次insert操作,所以在此不讨论
接下来,讲述每个操作的做法

  • 插入
    这个操作按一般平衡树操作就可以了

版本1(代码已删除):
我的代码中把log变成了sort的,rand值直接从上到下填即可

版本2(本文代码):
构造笛卡尔树,复杂度变成次数log加上数量了,减小常数

  • 删除
    直接旋出来那棵树,不过值得注意的是,需要对节点进行回收,否则空间复杂度受不了
  • 修改
    打个子树修改标记即可
  • 翻转
    打个子树修改标记即可
  • 求和
    记录子树权值和即可
  • 求和最大的子列
    记录子列中最大的子段,最大前缀,最大后缀
    值得注意的是,由于题目所述的区间长度必须大于0,所以要注意负权点的处理

这题看起来非常的简单,其实在具体实现过程中要注意各个标记之间的影响,需要实现好
另外,注意翻转标记下传是翻转状态,不然很容易调不出来
code

#include
#include
#include
#include
#include
#include
#define rg register
namespace fast_IO
{
     
	const int IN_LEN=1000000,OUT_LEN=1000000;
	char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
	inline char getchar_(){
     return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
	inline void putchar_(const char x){
     if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
	inline void flush(){
     fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
typedef long long ll;
template<typename T>inline T min(const T x,const T y){
     return x<y?x:y;}
template<typename T>inline T max(const T x,const T y){
     return x>y?x:y;}
template <typename T> inline void mind(T&a,const T b){
     a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){
     a=a>b?a:b;}
template<typename T>inline T abs(const T x){
     return x>0?x:-x;}
template<typename T>inline T gcd(const T a,const T b){
     if(!b)return a;return gcd(b,a%b);}
template<typename T>inline void swap(T&a,T&b){
     const T c=a;a=b;b=c;}
template<typename T>inline void read(T&x)
{
     
	char cu=getchar();bool fla=0;x=0;
	while(!isdigit(cu)){
     if(cu=='-')fla=1;cu=getchar();}
	while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
	if(fla)x=-x;
}
template<typename T>inline void printe(const T x)
{
     
	if(x>=10)printe(x/10);
	putchar(x%10+'0'); 
}
template<typename T>inline void print(const T x)
{
     
	if(x<0)putchar('-'),printe(-x);
	else printe(x); 
}
const int maxn=500001;
struct Point//平衡树节点 
{
     
	int x,key,lson,rson,size;
	int zuoda,youda,zhong,he;
	bool flag;int change;
}Q[maxn];int root;
void blbl(const int x)
{
     
	if(!x)return;
	printf("%d\n",Q[x].x);
	blbl(Q[x].lson);
	blbl(Q[x].rson);
}
int rubbish[maxn],top;
int newnode(const int R)//新建节点 传入rand 
{
     
	int usd=rubbish[top--];
	Q[usd]=(Point){
     0,R,0,0,1,0,0,0,0,0,-1001};
	return usd;
}
void change(Point&hos,const int x)//改变单点的x 
{
     
	hos.x=x;
	maxd(hos.zuoda,x),maxd(hos.youda,x);
	hos.zhong=x;
	hos.he+=x;
}
void deal_flag(Point&hos)//解决翻转 
{
     
	swap(hos.lson,hos.rson);
	swap(hos.zuoda,hos.youda);
	hos.flag^=1; 
}
void deal_change(Point&hos,const int x)//解决赋值 
{
     
	hos.x=x;
	if(x>0)hos.zuoda=hos.youda=hos.zhong=hos.he=hos.size*x;
	else hos.zuoda=hos.youda=0,hos.zhong=x,hos.he=hos.size*x;
	hos.change=x;
}
void down(Point&hos)//下传标记 
{
     
	int w=hos.lson;
	if(w)
	{
     
		if(hos.flag)deal_flag(Q[w]);
		if(hos.change!=-1001)deal_change(Q[w],hos.change);
	}
	w=hos.rson;
	if(w)
	{
     
		if(hos.flag)deal_flag(Q[w]);
		if(hos.change!=-1001)deal_change(Q[w],hos.change);
	}
	hos.flag=0,hos.change=-1001;
}
void update(Point&hos)//更新 
{
     
	Point&L=Q[hos.lson],&R=Q[hos.rson];
	hos.size=L.size+R.size+1;
	hos.he=L.he+R.he+hos.x;
	hos.zuoda=max(L.zuoda,L.he+hos.x+R.zuoda);
	hos.youda=max(R.youda,R.he+hos.x+L.youda);
	hos.zhong=max(max(L.zhong,R.zhong),L.youda+hos.x+R.zuoda);
}
std::pair<int,int> split(const int x,const int kth)
{
     
	std::pair<int,int>res(0,0);
	if(!x)return res;
	down(Q[x]);
	if(Q[Q[x].lson].size+1==kth)
	{
     
		res.first=x,res.second=Q[x].rson;
		Q[x].rson=0;
	}
	else if(Q[Q[x].lson].size+1<kth)
	{
     
		res=split(Q[x].rson,kth-(Q[Q[x].lson].size+1));
		Q[x].rson=res.first;
		res.first=x;
	}
	else
	{
     
		res=split(Q[x].lson,kth);
		Q[x].lson=res.second;
		res.second=x;
	}
	update(Q[x]);
	return res;
}
int merge(int x,int y)
{
     
	if(!x)return y;
	if(!y)return x;
	down(Q[x]),down(Q[y]);
	if(Q[x].key<Q[y].key)
	{
     
		Q[x].rson=merge(Q[x].rson,y);
		update(Q[x]);
		return x;
	}
	else
	{
     
		Q[y].lson=merge(x,Q[y].lson);
		update(Q[y]);
		return y;
	}
}
int m,P[maxn],Ptop;
char opt[11];int opttop;
int STA[maxn],Stop,ID[maxn];
int maker()
{
     
	int rando=rand();
	STA[Stop=1]=newnode(rando),ID[Stop]=P[1];
	for(rg int i=2;i<=Ptop;i++)
	{
     
		int ME=newnode(rando);
		rando=rand();
		bool fla=0;
		while(Stop>1&&rando<Q[STA[Stop]].key)Q[STA[Stop-1]].rson=STA[Stop],change(Q[STA[Stop]],ID[Stop]),update(Q[STA[Stop]]),Stop--,fla=1;
		if(rando<Q[STA[Stop]].key)
		{
     
			change(Q[STA[Stop]],ID[Stop]),update(Q[STA[Stop]]);
			Q[ME].lson=STA[Stop];
			STA[Stop]=ME,ID[Stop]=P[i];
		}
		else
		{
     
			if(fla)
			{
     
				Q[STA[Stop]].rson=0;
				Q[ME].lson=STA[Stop+1];
			}
			STA[++Stop]=ME,ID[Stop]=P[i];
		}
	}
	while(Stop>1)Q[STA[Stop-1]].rson=STA[Stop],change(Q[STA[Stop]],ID[Stop]),update(Q[STA[Stop]]),Stop--;
	change(Q[STA[Stop]],ID[Stop]),update(Q[STA[Stop]]);
	return STA[1];
}
void insert(const int x)
{
     
	std::pair<int,int>T=split(root,x);
	root=merge(merge(T.first,maker()),T.second);
}
void rerubbish(const int x)
{
     
	if(!x)return;
	rubbish[++top]=x;
	rerubbish(Q[x].lson),rerubbish(Q[x].rson);
}
void del(const int x)
{
     
	std::pair<int,int>T=split(root,x),S=split(T.second,Ptop);
	rerubbish(S.first);
	root=merge(T.first,S.second);
}
int samv;
void sam(const int x)
{
     
	std::pair<int,int>T=split(root,x),S=split(T.second,Ptop);
	deal_change(Q[S.first],samv);
	root=merge(merge(T.first,S.first),S.second);
}
void reverse(const int x)
{
     
	std::pair<int,int>T=split(root,x),S=split(T.second,Ptop);
	deal_flag(Q[S.first]);
	root=merge(merge(T.first,S.first),S.second);
}
int sum(const int x)//complete
{
     
	std::pair<int,int>T=split(root,x),S=split(T.second,Ptop);
	int res=Q[S.first].he;
	root=merge(merge(T.first,S.first),S.second);
	return res;
}
int bigv(const int x)//compete
{
     
	return Q[root].zhong;
}
int main()
{
     
	Q[0].zhong=-(0x7f7f7f7f);
	for(rg int i=1;i<=500000;i++)rubbish[++top]=i;
	read(Ptop),read(m),srand(time(0));
	for(rg int i=1;i<=Ptop;i++)read(P[i]);
	insert(0);
	for(rg int i=1;i<=m;i++)
	{
     
		int x;
		opt[opttop=1]=getchar();
		while(opt[1]<'A'||opt[1]>'Z')opt[1]=getchar();
		while(opt[opttop]!=' '&&opt[opttop]!='\n'&&opt[opttop]!=EOF)opt[++opttop]=getchar();
		if(opt[1]=='I')
		{
     
			read(x),read(Ptop);
			for(rg int i=1;i<=Ptop;i++)read(P[i]);
			insert(x);
		}
		else if(opt[1]=='D')
		{
     
			read(x),read(Ptop);x--;
			del(x);
		} 
		else if(opt[3]=='K')
		{
     
			read(x),read(Ptop),read(samv);x--;
			sam(x);
		}
		else if(opt[1]=='R')
		{
     
			read(x),read(Ptop);x--;
			reverse(x);
		}
		else if(opt[1]=='G')
		{
     
			read(x),read(Ptop);x--;
			print(sum(x)),putchar('\n');
		}
		else print(bigv(root)),putchar('\n');
	}
	return flush(),0;
}

总结

一道较为繁琐的数据结构,调了有一会儿,马上就要csp了,还得加强代码熟练度

你可能感兴趣的:(OI,平衡树)