[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196

 

题目分析

区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现。

为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = =

嗯就是这样,代码长度= =我写了260行......Debug了n小时= =

 

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
using namespace std;

const int MaxN = 50000 + 5, MaxM = 50000 + 5, MN = 100000 + 15, INF = 999999999, MaxNode = 8000000 + 15;

int n, m, Index, Used_Index, Top, Hash_Index;
int A[MaxN], Root[MaxN], T[MaxNode], Son[MaxNode][2], U[MaxN], C[MaxN], Que[MaxN + MaxM], TR[MaxN + MaxM];

struct Query
{
	int f, L, R, k, Num, Pos;
} Q[MaxM];

map<int, int> M;

multiset<int> S[MaxN * 4];
multiset<int>::iterator It;

inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;}

void Add(int &x, int s, int t, int Pos, int Num) 
{
	if (x == 0) x = ++Index;
	T[x] += Num;
	if (s == t) return;
	int m = (s + t) >> 1;
	if (Pos <= m) Add(Son[x][0], s, m, Pos, Num);
	else Add(Son[x][1], m + 1, t, Pos, Num);
}

void Change(int x, int Pos, int Num) 
{
	for (int i = x; i <= n; i += i & -i)
		Add(Root[i], 0, MN, Pos, Num);
}

void Add_S(int x, int s, int t, int Pos, int Num) 
{
	S[x].insert(Num);
	if (s == t) return;
	int m = (s + t) >> 1;
	if (Pos <= m) Add_S(x << 1, s, m, Pos, Num);
	else Add_S(x << 1 | 1, m + 1, t, Pos, Num);
}

void Del_S(int x, int s, int t, int Pos, int Num)
{
	S[x].erase(S[x].find(Num));
	if (s == t) return;
	int m = (s + t) >> 1;
	if (Pos <= m) Del_S(x << 1, s, m, Pos, Num);
	else Del_S(x << 1 | 1, m + 1, t, Pos, Num);
}
 
void Init_U(int x) 
{
	for (int i = x; i; i -= i & -i)
		U[i] = Root[i];
}

void Turn(int x, int f) 
{
	for (int i = x; i; i -= i & -i)
	{
		if (C[i] == Used_Index) break;
		C[i] = Used_Index;
		U[i] = Son[U[i]][f];
	}
}

int Get_LSum(int x) 
{
	int ret = 0;
	for (int i = x; i; i -= i & -i)
		ret += T[Son[U[i]][0]];
	return ret;
}

int Before(int x, int s, int t, int l, int r, int Num)
{
	int ret;
	if (l <= s && r >= t)
	{
		It = S[x].end();
		It--;
		if (*It < Num) return *It;
		It = S[x].begin();
		if (*It >= Num) return -INF;
		It = S[x].lower_bound(Num);
		It--;
		return *It;
	}
	int m = (s + t) >> 1;
	ret = -INF;
	if (l <= m) ret = gmax(ret, Before(x << 1, s, m, l, r, Num));
	if (r >= m + 1) ret = gmax(ret, Before(x << 1 | 1, m + 1, t, l, r, Num));
	return ret;
}

int After(int x, int s, int t, int l, int r, int Num)
{
	int ret;
	if (l <= s && r >= t)
	{
		It = S[x].upper_bound(Num);
		if (It == S[x].end()) return INF;
		else return *It;
	}
	int m = (s + t) >> 1;
	ret = INF;
	if (l <= m) ret = gmin(ret, After(x << 1, s, m, l, r, Num));
	if (r >= m + 1) ret = gmin(ret, After(x << 1 | 1, m + 1, t, l, r, Num));
	return ret;
}

int main() 
{
	scanf("%d%d", &n, &m);
	Top = 0; Index = 0;
	for (int i = 1; i <= n; ++i) 
	{
		scanf("%d", &A[i]);
		Que[++Top] = A[i];
	}
	for (int i = 1; i <= m; ++i) 
	{
		scanf("%d", &Q[i].f);
		switch (Q[i].f) 
		{
			case 1 : 
				scanf("%d%d%d", &Q[i].L, &Q[i].R, &Q[i].Num);
				break;
			case 2 : 
				scanf("%d%d%d", &Q[i].L, &Q[i].R, &Q[i].k);
				break;
			case 3 :
				scanf("%d%d", &Q[i].Pos, &Q[i].Num);
				break;
			case 4 :
				scanf("%d%d%d", &Q[i].L, &Q[i].R, &Q[i].Num);
				break;
			case 5 :
				scanf("%d%d%d", &Q[i].L, &Q[i].R, &Q[i].Num);
				break;
		}
		if (Q[i].f != 2) Que[++Top] = Q[i].Num;
	}
	sort(Que + 1, Que + Top + 1);
	Hash_Index = 0;
	for (int i = 1; i <= Top; ++i) 
	{
		if (i > 1 && Que[i] == Que[i - 1]) continue;
		M[Que[i]] = ++Hash_Index;
		TR[Hash_Index] = Que[i];
	}
	for (int i = 1; i <= n; ++i) 
	{
		A[i] = M[A[i]];
		Change(i, A[i], 1);
		Add_S(1, 1, n, i, A[i]);
	}
	int L, R, Pos, Num, k, Temp, l, r, mid; 
	for (int i = 1; i <= m; ++i) 
	{
		if (Q[i].f != 2) Q[i].Num = M[Q[i].Num];
		switch (Q[i].f) 
		{
			case 1 : 
				L = Q[i].L; R = Q[i].R; Num = Q[i].Num;
				Used_Index = 0;
				Init_U(L - 1);
				Init_U(R);
				Temp = 0;
				l = 0; r = MN;
				while (l < r) 
				{
					++Used_Index;
					mid = (l + r) >> 1;
					if (Num <= mid) 
					{
						r = mid;
						Turn(L - 1, 0);
						Turn(R, 0);
					}
					else 
					{
						Temp += Get_LSum(R) - Get_LSum(L - 1);
						l = mid + 1;
						Turn(L - 1, 1);
						Turn(R, 1);
					}
				}
				printf("%d\n", Temp + 1);
				break;

			case 2 : 
				L = Q[i].L; R = Q[i].R; k = Q[i].k;
				Init_U(L - 1);
				Init_U(R);
				Used_Index = 0;
				Temp = 0;
				l = 0; r = MN;
				while (l < r)
				{
					++Used_Index;
					mid = (l + r) >> 1;
					Temp = Get_LSum(R) - Get_LSum(L - 1);
					if (Temp >= k) 
					{
						r = mid;
						Turn(L - 1, 0);
						Turn(R, 0);
					}
					else 
					{
						l = mid + 1;
						Turn(L - 1, 1);
						Turn(R, 1);
						k -= Temp; 
					}
				}
				printf("%d\n", TR[l]);
				break;
			
			case 3 :
				Pos = Q[i].Pos; Num = Q[i].Num;
				Change(Pos, A[Pos], -1);
				Del_S(1, 1, n, Pos, A[Pos]);
				A[Pos] = Num;
				Change(Pos, Num, 1);
				Add_S(1, 1, n, Pos, Num);
				break;
				
			case 4 :
				L = Q[i].L; R = Q[i].R; Num = Q[i].Num;
				printf("%d\n", TR[Before(1, 1, n, L, R, Num)]);
				break;
			
			case 5 :
				L = Q[i].L; R = Q[i].R; Num = Q[i].Num;
				printf("%d\n", TR[After(1, 1, n, L, R, Num)]);
				break;
		}
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/JoeFan/p/4318950.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值