BZOJ3065 带插入区间K小值

9 篇文章 0 订阅
7 篇文章 0 订阅

因为要求支持插入,所以里层可以套上一个平衡树来维护对应位置的信息。

一般来说平衡树各项操作都是O(logN)的,但是由于外层要维护一个线段树,那么带旋转的平衡树复杂度就难以保证,因为每动一个节点就要在线段树中插入这个节点的子树大小个数的点。(点的深度和子树大小负相关)

带旋转的平衡树最坏情况每次都调整某个点到根的路径,而不带旋转的替罪羊树则是调整整棵子树。

所以带旋转的平衡树中深度越小的点越可能被调整,替罪羊树中深度越小的点越稳定,因此里层平衡树建议选用替罪羊树。同时注意替罪羊树的重构操作时可以用线段树合并,二分答案时可以找到若干棵线段树一起来二分,这样复杂度可以做到O(NLogNLogN)。

值得吐槽的是,没写线段树合并暴力插入节点用了36秒,写线段树合并用了35秒,是我的姿势不对么?


代码看上去有些长?(实际上不长)


代码如下:

/* 
* @Author: duyixian
* @Date:   2016-01-18 17:29:04
* @Last Modified by:   duyixian
* @Last Modified time: 2016-01-22 10:08:40
*/

#include "cstdio"
#include "cstdlib"
#include "iostream"
#include "algorithm"
#include "cstring"
#include "queue"
#include "vector"
#include "cmath"

using namespace std;

#define MAX_SIZE 
#define INF 70005
#define Eps
#define Mod
#define Get(x, a) (x ? x -> a : 0)
#define L(i) (i ? Mid + 1 : Left)
#define R(i) (i ? Right : Mid)
#define Alpha 0.75
#define Traveling(x) for(typeof(x.begin()) it = x.begin(); it != x.end(); ++it)

inline int Get_Int()
{
	int Num = 0, Flag = 1;
	char ch;
	do
	{
		ch = getchar();
		if(ch == '-')
			Flag *= -1;
	}
	while(ch < '0' || ch > '9');
	do
	{
		Num = Num * 10 + ch - '0';
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9');
	return Num * Flag;
}

class Segment_Tree_Node
{
public:
	Segment_Tree_Node *Child[2];
	int Sum;
};

class Segment_Tree
{
public:
	Segment_Tree_Node *Root;

	inline Segment_Tree_Node* New()
	{
		return (Segment_Tree_Node*)calloc(1, sizeof(Segment_Tree_Node));
	}

	inline void Reclaim(Segment_Tree_Node *&x)
	{
		if(x -> Sum)
			return;
		free(x);
		x = NULL;
	}

	void Clear(Segment_Tree_Node *x)
	{
		if(!x)
			return;
		Clear(x -> Child[0]);
		Clear(x -> Child[1]);
		free(x);
	}

	inline void Clear()
	{
		Clear(Root);
		Root = NULL;
	}

	Segment_Tree_Node* Merge(Segment_Tree_Node *x, Segment_Tree_Node *y, int Left, int Right)
	{
		if(!y)
			return x;
		if(!x)
			x = New();
		if(Left == Right)
		{
			x -> Sum += Get(y, Sum);
			Reclaim(x);
			return x;
		}
		int Mid = Left + Right >> 1;
		x -> Child[0] = Merge(x -> Child[0], y -> Child[0], L(0), R(0));
		x -> Child[1] = Merge(x -> Child[1], y -> Child[1], L(1), R(1));
		x -> Sum = Get(x -> Child[0], Sum) + Get(x -> Child[1], Sum);
		Reclaim(x);
		return x;
	}

	inline void Merge(Segment_Tree x)
	{
		Root = Merge(Root, x.Root, 0, INF);
	}

	Segment_Tree_Node* Insert(Segment_Tree_Node *x, int Location, int Value, int Left, int Right)
	{
		if(!x)
			x = New();
		if(Left == Right)
		{
			x -> Sum += Value;
			return x;
		}
		int Mid = Left + Right >> 1, i = Location > Mid;
		x -> Child[i] = Insert(x -> Child[i], Location, Value, L(i), R(i));
		x -> Sum = Get(x -> Child[0], Sum) + Get(x -> Child[1], Sum);
		Reclaim(x);
		return x;
	}

	inline void Insert(int Location, int Value)
	{
		Root = Insert(Root, Location, Value, 0, INF);
	}
};

class Scapegoat_Tree_Node
{
public:
	Scapegoat_Tree_Node *Child[2], *Father;
	int Size, Value;
	Segment_Tree Sum;

	inline bool UnBalanced()
	{
		return Get(Child[0], Size) > Alpha * Size || Get(Child[1], Size) > Alpha * Size;
	}

	inline void Update()
	{
		if(Child[0])
			Sum.Merge(Child[0] -> Sum);
		if(Child[1])
			Sum.Merge(Child[1] -> Sum);
	}
};

class Scapegoat_Tree
{
public:
	Scapegoat_Tree_Node *Root, *Good;

	vector<Scapegoat_Tree_Node*> temp;
	vector<Segment_Tree_Node*> Tree;
	vector<int> Val;

	inline Scapegoat_Tree_Node* New(int Value)
	{
		Scapegoat_Tree_Node *x = (Scapegoat_Tree_Node*)calloc(1, sizeof(Scapegoat_Tree_Node));
		x -> Sum.Insert(Value, 1);
		x -> Value = Value;
		x -> Size = 1;
		return x;
	}

	Scapegoat_Tree()
	{
		Good = (Scapegoat_Tree_Node*)malloc(sizeof(Scapegoat_Tree_Node));
	}
	
	void Travel(Scapegoat_Tree_Node *x)
	{
		if(!x)
			return;
		Travel(x -> Child[0]);
		temp.push_back(x);
		Travel(x -> Child[1]);
	}

	Scapegoat_Tree_Node* Rebuild(int Left, int Right, Scapegoat_Tree_Node *Father)
	{
		if(Left > Right)
			return NULL;
		int Mid = Left + Right >> 1;
		Scapegoat_Tree_Node *x = temp[Mid];
		x -> Size = Right - Left + 1;
		x -> Sum.Insert(x -> Value, 1);
		x -> Child[0] = Rebuild(Left, Mid - 1, x);
		x -> Child[1] = Rebuild(Mid + 1, Right, x);
		x -> Update();
		x -> Father = Father;
		return x;
	}

	inline void Rebuild(Scapegoat_Tree_Node *x)
	{
		if(!x || x == Good)
			return;
		Scapegoat_Tree_Node *Father = x -> Father;
		Travel(x);
		Traveling(temp)
			(*it) -> Sum.Clear();
		if(Father)
			Father -> Child[Father -> Child[1] == x] = Rebuild(0, temp.size() - 1, Father);
		else
			Root = Rebuild(0, temp.size() - 1, Father);
		temp.clear();
	}

	inline Scapegoat_Tree_Node* Insert_and_Find_Scapegoat_if_Need(Scapegoat_Tree_Node *x, int Location, int Value, int Depth)
	{
		++x -> Size;
		x -> Sum.Insert(Value, 1);
		int i = Location > Get(x -> Child[0], Size) + 1;
		if(i)
			Location -= Get(x -> Child[0], Size) + 1;
		if(x -> Child[i])
		{
			Scapegoat_Tree_Node *Scapegoat = Insert_and_Find_Scapegoat_if_Need(x -> Child[i], Location, Value, Depth + 1);
			if(Scapegoat)
				return Scapegoat;
			else
				return x -> UnBalanced() ? x : NULL;
		}
		else
		{
			x -> Child[i] = New(Value);
			x -> Child[i] -> Father = x;
			return Depth <= log(Root -> Size) / log(1 / Alpha) ? Good : NULL;
		}
	}

	inline void Insert(int Location, int Value)
	{
		if(!Root)
			Root = New(Value);
		else 
			Rebuild(Insert_and_Find_Scapegoat_if_Need(Root, Location, Value, 1));
	}

	inline void Change(int Location, int Value)
	{
		Scapegoat_Tree_Node *x = Root;
		while(x)
		{
			if(Location <= Get(x -> Child[0], Size))
				x = x -> Child[0];
			else if(Location == Get(x -> Child[0], Size) + 1)
				break;
			else
				Location -= Get(x -> Child[0], Size) + 1, x = x -> Child[1];
		}
		int K = x -> Value;
		x -> Value = Value;
		for(; x; x = x -> Father)
			x -> Sum.Insert(Value, 1), x -> Sum.Insert(K, -1);
	}

	void Find(Scapegoat_Tree_Node *x, int Left, int Right)
	{
		if(Left > Right)
			return;
		int L = Get(x -> Child[0], Size);
		if(Left == 1 && Right == x -> Size)
		{
			Tree.push_back(x -> Sum.Root);
			return;
		}
		if(Right <= L)
			Find(x -> Child[0], Left, Right);
		else if(Left > L + 1)
			Find(x -> Child[1], Left - L - 1, Right - L - 1);
		else
		{
			Val.push_back(x -> Value);
			Find(x -> Child[1], 1, Right - L - 1);
			Find(x -> Child[0], Left, L);
		}
	}

	inline int Ask(int Left, int Right, int K)
	{
		Find(Root, Left, Right);
		int L = 0, R = INF, Sum = 0;
		while(L < R)
		{
			int Sum1 = 0, Sum2 = 0, M = L + R >> 1;
			Traveling(Tree)
				Sum1 += Get(Get((*it), Child[0]), Sum);
			Traveling(Val)
				if((*it) <= M)
					++Sum2;
			if(Sum + Sum1 + Sum2 < K)
			{
				Sum += Sum1;
				Traveling(Tree)
					(*it) = Get((*it), Child[1]);
				L = M + 1;
			}
			else
			{
				Traveling(Tree)
					*it = Get((*it), Child[0]);
				R = M;
			}
		}
		Tree.clear();
		Val.clear();
		return L;
	}

}Tree;

int LastAns, N, Q;

int main()
{
	cin >> N;
	for(int i = 0; i < N; ++i)
		Tree.temp.push_back(Tree.New(Get_Int()));
	Traveling(Tree.temp)
		(*it) -> Sum.Clear();
	Tree.Root = Tree.Rebuild(0, Tree.temp.size() - 1, NULL);
	Tree.temp.clear();
	cin >> Q;
	while(Q--)
	{
		char Op[3];
		scanf("%s", Op);
		int a = Get_Int() ^ LastAns, b = Get_Int() ^ LastAns;
		if(Op[0] == 'Q')
			printf("%d\n", LastAns = Tree.Ask(a, b, Get_Int() ^ LastAns));
		else if(Op[0] == 'M')
			Tree.Change(a, b);
		else
			Tree.Insert(a, b);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值