题目大意
一些不同颜色的兔子排成一排,要求支持以下操作: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; }