[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;
}

  

时间: 2024-08-24 07:22:14

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】的相关文章

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树

[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include <string> #include <alg

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

[BZOJ 2141][国家集训队 2011]排队 树状数组套平衡树

这道题也就是一个动态逆序对嘛,本质上就是个查询区间排名 刚刚打了一道 [CQOI 2011]动态逆序对  用的线段树套平衡树,代码如下: #include<iostream> #include<cstdio> #include<cstring> using namespace std; #define pos(i,a,b) for(int i=(a);i<=(b);i++) #define N 101000 #include<cstdlib> #def

洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)

洛谷题目传送门 emm...题目名写了个平衡树,但是这道题的理论复杂度最优解应该还是树状数组套值域线段树吧. 就像dynamic ranking那样(蒟蒻的Sol,放一个link骗访问量233) 所有的值(包括初始a数组,操作1.3.4.5的k)全部先丢进去离散化 对于1操作查比它小的数,挑log棵线段树,找区间小于这个数的个数+1,这个还比较好像 操作2就是dynamic ranking,log棵线段树一起加减,像静态主席树求第k小一样跳,操作3 dynamic ranking里也有 操作4先

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

zoj2112 Dynamic Rankings 动态区间第k大,树状数组套平衡树

#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int

Codeforces 1139F Dish Shopping 树状数组套平衡树 || 平衡树

Dish Shopping 将每个物品拆成p 和 s 再加上人排序. 然后问题就变成了, 对于一个线段(L - R), 问有多少个(li, ri)满足  L >= li && R >= ri, 这个东西可以直接树状数组套平衡树维护. 但是这个题目有个特殊性,因为排好序之后不会存在 li > L && ri > R的点, 所以可以直接 用平衡树, 或者线段树去维护这个东西. 平板电视 #include<bits/stdc++.h> #inc