题目大意
一些不同颜色的兔子排成一排,要求支持以下操作: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
", 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
", _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
");
else
{
pl = LowerBound(colorL, colorR, posL, GetPos);
pr = UpperBound(colorL, colorR, posR, GetPos);
if (pl == NO_ANS || pr == NO_ANS)
printf("0
");
else
printf("%d
", 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;
}