luogu3939 数颜色

题目大意

一些不同颜色的兔子排成一排,要求支持以下操作:1.查询区间[l,r]间颜色为c的兔子的数量。2.将位置p和p+1的兔子交换位置。兔子数<=3*1e5,操作数<=3*1e5。

思路

对于每一种颜色维护一段可查询修改的区间。但是我们无法对于每个3*1e5个颜色都直接维护一个代表着3*1e5个位置的区间,内存受不了。故每一个颜色,有以下思路。

只处理部分区间

没法处理整个区间,可以只对操作的区间进行处理呀!

线段树动态开点

动态开点正好达到了这个要求。

注意

  • 不要PullUp!单点修改是不需要PullUp的,因为修改时到达的节点都包含所要修改的点,所以到一个节点修改一次即可。而要PullUp,系统调用堆栈会浪费时间。
  • 多个线段树所共有的量尽量设为全局变量,比如区间长度N。本题第二个测试点对最后一个兔子进行2操作,如果一个线段树一个N,管理颜色0的线段树中N=0,导致RE。这使程序鲁棒性不强。
#include <cstdio>
#include <cassert>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_N = 300010, MAX_NODE = MAX_N * 30;
int N;

struct Node
{
	Node *LeftSon, *RightSon;
	int Key;
}_nodes[MAX_NODE];
int Cnt = 0;

Node *NewNode()
{
	return _nodes + (++Cnt);
}

struct RangeTree
{
private:
	Node *Root;

	void Update(Node *&cur, int l, int r, int p, int delta)
	{
		//printf("l %d r %d p %d\n", l, r, p);
		if (!cur)
			cur = NewNode();
		cur->Key += delta;
		if (l == r)
			return;
		int mid = (l + r) >> 1;
		if (p <= mid)
			Update(cur->LeftSon, l, mid, p, delta);
		if (p > mid)
			Update(cur->RightSon, mid + 1, r, p, delta);
	}

	int Query(Node *cur, int sl, int sr, int al, int ar)
	{
		if (!cur)
			return 0;
		if (al <= sl && sr <= ar)
			return cur->Key;
		int mid = (sl + sr) >> 1, ans = 0;
		if (al <= mid)
			ans += Query(cur->LeftSon, sl, mid, al, ar);
		if (ar > mid)
			ans += Query(cur->RightSon, mid + 1, sr, al, ar);
		return ans;
	}

public:
	RangeTree():Root(NULL){}

	void Update(int p, int delta)
	{
		Update(Root, 1, N, p, delta);
	}

	int Query(int l, int r)
	{
		return Query(Root, 1, N, l, r);
	}
}_trees[MAX_N];

int main()
{
	memset(_nodes, 0, sizeof(_nodes));
	static int PosColor[MAX_N];
	int opCnt;
	scanf("%d%d", &N, &opCnt);
	for (int i = 1; i <= N; i++)
	{
		int color;
		scanf("%d", &color);
		PosColor[i] = color;
		_trees[color].Update(i, 1);
	}
	while (opCnt--)
	{
		int op, l, r, color;
		scanf("%d", &op);
		switch (op)
		{
		case 1:
			scanf("%d%d%d", &l, &r, &color);
			printf("%d\n", _trees[color].Query(l, r));
			break;
		case 2:
			scanf("%d", &l);
			_trees[PosColor[l]].Update(l, -1);
			_trees[PosColor[l]].Update(l + 1, +1);
			_trees[PosColor[l + 1]].Update(l, +1);
			_trees[PosColor[l + 1]].Update(l + 1, -1);
			swap(PosColor[l], PosColor[l + 1]);
			break;
		}
	}
	return 0;

}

 

将兔子的位置作为值,位置大小的排名作为下标

兔子只有3*1e5个,所以这样做肯定没问题。

以下所说“位置”指的是值,“排名”指的是下标。

Splay

Splay维护的就是一个值单调递增的区间,支持插入、删除、查找前驱和后继操作,故可行。

二分

维护一个大小为3*1e5的数组,保存兔子的信息:颜色和位置。在该数组中,我们要把颜色相同的兔子放在一块,并让同一颜色的兔子的位置大小单调递增,也就是以颜色为第一关键字,位置为第二关键字排序。这样,我们先找到颜色所在区间,然后再在该区间内找到值域 包含于要查询的位置区间的 排名区间即可得出结果,方法为用LowerBound和UpperBound二分搜索左端点和右端点。修改时,若修改的相邻位置的颜色不同,分别改两个颜色对应兔子的位置;若相同,则不用操作,否则不满足位置单调递增了。

注意

二分查找颜色区间时,可能区间内要查询的颜色一个都没有,此时要特殊判定。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_RAB = 300010, NO_ANS = -1;
int TotRab;
int A[MAX_RAB];

#define LOOP(i, n) for(int i=1; i<=n; i++)

struct Rab
{
	int Color, Pos;
	Rab(){}
	Rab(int color, int pos):Color(color),Pos(pos){}
	bool operator < (const Rab a) const
	{
		if (Color != a.Color)
			return Color < a.Color;
		else
			return Pos < a.Pos;
	}
}_rabs[MAX_RAB];

int LowerBound(int l, int r, int key, int(*GetVal)(int))
{
	if (key > GetVal(r))
		return NO_ANS;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (key <= GetVal(mid))
			r = mid;
		else
			l = mid + 1;
	}
	return l;
}

int UpperBound(int l, int r, int key, int(*GetVal)(int))
{
	if (key < GetVal(l))
		return NO_ANS;
	while (l < r)
	{
		int mid = (l + r + 1) / 2;
		if (key >= GetVal(mid))
			l = mid;
		else
			r = mid - 1;
	}
	return l;
}

int GetColor(int p)
{
	return _rabs[p].Color;
}

int GetPos(int p)
{
	return _rabs[p].Pos;
}

int main()
{
	int opCnt, color, op, posL, posR, pl, pr, p, lColor, rColor, colorL, colorR;
	scanf("%d%d", &TotRab, &opCnt);
	LOOP(i, TotRab)
	{
		scanf("%d", &color);
		_rabs[i] = Rab(color, i);
		A[i] = color;
	}
	sort(_rabs + 1, _rabs + TotRab + 1);
	while (opCnt--)
	{
		scanf("%d", &op);
		switch (op)
		{
		case 1://Query
			scanf("%d%d%d", &posL, &posR, &color);
			colorL = LowerBound(1, TotRab, color, GetColor);
			colorR = UpperBound(1, TotRab, color, GetColor);
			if (_rabs[colorL].Color!=color || _rabs[colorR].Color!=color)
				printf("0\n");
			else
			{
				pl = LowerBound(colorL, colorR, posL, GetPos);
				pr = UpperBound(colorL, colorR, posR, GetPos);
				if (pl == NO_ANS || pr == NO_ANS)
					printf("0\n");
				else
					printf("%d\n", pr - pl + 1);
			}
			break;
		case 2://Modify
			scanf("%d", &posL);
			posR = posL + 1;
			lColor = A[posL];
			rColor = A[posR];
			if (lColor != rColor)
			{
				swap(A[posL], A[posR]);
				colorL = LowerBound(1, TotRab, lColor, GetColor);
				colorR = UpperBound(1, TotRab, lColor, GetColor);
				pl = LowerBound(colorL, colorR, posL, GetPos);
				colorL = LowerBound(1, TotRab, rColor, GetColor);
				colorR = UpperBound(1, TotRab, rColor, GetColor);
				pr = LowerBound(colorL, colorR, posR, GetPos);
				_rabs[pl].Pos++;
				_rabs[pr].Pos--;
			}
			break;
		}
	}
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/headboy2002/p/8993035.html