有一个方阵,在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:
计算每一次离队事件中,离队的同学的编号是多少。n,m,q<=3*10^5。
直接存储空间太大。经观察发现,一排中有很多同学的编号是连续的,因此我们可以用一个维护(beginId,lastId)的节点来表示多个同学的位置状态。因此我们可以对每一排(不包括一排中位于最右一列的位置)和最后一列各运用一棵SplayTree。
每个节点内的值Val(beginId,lastId)表示编号范围为[beginId,lastId]的同学的位置是连续的,这些同学(以后简称同学块)都由该节点维护。
拿一排来说,这一排的Splay树内的所有节点所维护的同学块组成了这一排内的所有同学。
拿一排来说,关键字是每个节点所维护的同学块的在排中的位置。
如果该同学不在最后一列,则在维护它所在排的SplayTree中输入同学的所在列,得到维护该同学的节点,然后将维护该同学的节点分成两半,一半维护原节点所维护同学块在该同学左面的那部分,一半维护右半部分。随后将最后一列同排的同学从维护最后一列的SplayTree中删除,将其加入到维护该排的SplayTree中的末尾,最后将出队的同学加入到维护最后一列的SplayTree中的末尾。如果同学本来就在最后一列,前半部分步骤就可以省掉了。
从我们对SplayTree操作的特殊性入手。如果我们给每个节点安排一个Key值,那么其含义应当是:其所维护的区间块中第一个同学所在的位置。但是只要从树中删除一个节点,以后节点的Key值就都要变化了。因此在本题中无法维护Key值。也就是说,决定这个树的排列顺序的只能是节点所维护的同学块间的相对位置,而不是绝对位置。所以我们无法GetNodeByKey。
在SplayTree的实现过程中,同学块的存在等价于:同学块内的每一个同学的编号都能映射到一个假想的值上,那么就相当于一个同学块内的同学在假想的值这方面都并列,在这个节点中假想的值重叠的个数Cnt便是这个节点所维护的同学块中同学的数量。因此,拿一排来说,把一位同学所在列数作为它在树中的排名,我们就可以通过FindKth来找到维护这位同学的节点。
我们发现往树中插入值时,总是从树的末尾插入。而用到Key值总是在树的任意一个地方插入节点时才需要。
把左面的点保持原位,右面的点设为它的后继(如果原节点无右孩子,则设为右孩子,否则将GetNextNode()与右节点设为左父子)。(不要忘了设置cnt和size,且先设右面的点,再设左面的点,因为设置右面的点时可能需要用到左面的点的值)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cassert>
using namespace std;
#define int long long
const int MAX_ROW = 300010;
int TotRow, TotCol;
struct IdRange
{
int BeginId, LastId;
IdRange(int beginId, int lastId) :BeginId(beginId), LastId(lastId) {}
IdRange() {}
};
template<class ValType>
struct SplayTree
{
private:
struct Node
{
ValType Val;
Node *LeftSon, *RightSon, *Father;
int Cnt, Size;
Node(ValType val, int cnt, Node *father) :Val(val), Father(father), Cnt(cnt), Size(cnt), LeftSon(NULL), RightSon(NULL) {}
bool IsRoot()
{
return Father == NULL || (Father->LeftSon != this && Father->RightSon != this);
}
bool IsLeftSon()
{
assert(!IsRoot());
return Father->LeftSon == this;
}
void Refresh()
{
Size = (LeftSon ? LeftSon->Size : 0) + (RightSon ? RightSon->Size : 0) + Cnt;
}
};
Node *Root;
void SetRoot(Node *cur)
{
Root = cur;
if (Root)
Root->Father = NULL;
}
void Rotate(Node *cur)
{
Node *gfa = cur->Father->Father;
Node **gfaSon = cur->Father->IsRoot() ? &Root : cur->Father->IsLeftSon() ? &gfa->LeftSon : &gfa->RightSon;
Node **faSon = cur->IsLeftSon() ? &cur->Father->LeftSon : &cur->Father->RightSon;
Node **curSon = cur->IsLeftSon() ? &cur->RightSon : &cur->LeftSon;
*faSon = *curSon;
if (*faSon)
(*faSon)->Father = cur->Father;
*curSon = cur->Father;
(*curSon)->Father = cur;
*gfaSon = cur;
(*gfaSon)->Father = gfa;
(*curSon)->Refresh();
cur->Refresh();
}
void Splay(Node *cur)
{
while (!cur->IsRoot())
{
if (!cur->Father->IsRoot())
Rotate(cur->IsLeftSon() == cur->Father->IsLeftSon() ? cur->Father : cur);
Rotate(cur);
}
}
pair<Node*, int> FindKth(Node *cur, int rank)//pair中int指rank对应数据有并列时,rank与并列最高排名的距离[1...cutCnt]
{
while (true)
{
assert(cur);
int leftSize = cur->LeftSon ? cur->LeftSon->Size : 0, rootSize;
if (rank <= leftSize)
cur = cur->LeftSon;
else if (rank <= (rootSize = leftSize + cur->Cnt))
return pair<Node*, int>(cur, rank - leftSize);
else
{
cur = cur->RightSon;
rank -= rootSize;
}
}
}
void InsertBack(Node *&root, ValType val, int cnt)
{
Node **cur = &root, *prev = root ? root->Father : NULL;
while (*cur)
{
prev = *cur;
cur = &(*cur)->RightSon;
}
*cur = new Node(val, cnt, prev);
Splay(*cur);
}
Node *GetPrevNode(Node *cur)
{
cur = cur->LeftSon;
if (!cur)
return NULL;
while (cur->RightSon)
cur = cur->RightSon;
return cur;
}
Node *GetNextNode(Node *cur)
{
cur = cur->RightSon;
if (!cur)
return NULL;
while (cur->LeftSon)
cur = cur->LeftSon;
return cur;
}
void DeleteNode(Node *cur)
{
Splay(cur);
if (!cur->LeftSon || !cur->RightSon)
SetRoot(cur->LeftSon ? cur->LeftSon : cur->RightSon);
else if (cur->LeftSon && cur->RightSon)
{
Node *NewRoot = GetPrevNode(cur);
Splay(NewRoot);
assert(NewRoot->RightSon == cur && cur->LeftSon == NULL);
NewRoot->RightSon = cur->RightSon;
if (NewRoot->RightSon)
NewRoot->RightSon->Father = NewRoot;
}
}
public:
void InsertBack(ValType val, int cnt)
{
InsertBack(Root, val, cnt);
}
pair<ValType, int> FindKth(int rank)//pair中int指rank对应数据有并列时,rank与并列最高排名的距离[1...cutCnt]
{
pair<Node*, int> state = FindKth(Root, rank);
Node *find = state.first;
Splay(find);
return pair<ValType, int>(find->Val, state.second);
}
void Split(int rank, int cutCnt, ValType*(*SplitVal)(ValType&, int), bool(*IsNull)(ValType&))//把并列的分为[1...curCnt-1]和[cutCnt+1...cnt]两部分
{
pair<Node*, int> state = FindKth(Root, rank);
Node *cur = state.first;
ValType* a = SplitVal(cur->Val, cutCnt);
if (IsNull(a[0]) && !IsNull(a[1]))
{
swap(a[0], a[1]);
cutCnt = cur->Cnt - cutCnt + 1;
}
if (IsNull(a[0]))
DeleteNode(cur);
else
{
Node *newNode = NULL;
if (!IsNull(a[1]))
{
Node *nextFa = GetNextNode(cur);
if (nextFa == NULL)
{
newNode = new Node(a[1], cur->Cnt - cutCnt, cur);
cur->RightSon = newNode;
}
else
{
newNode = new Node(a[1], cur->Cnt - cutCnt, nextFa);
nextFa->LeftSon = newNode;
}
}
cur->Val = a[0];
cur->Cnt = cutCnt - 1;
Splay(newNode ? newNode : cur);
}
}
};
SplayTree<IdRange> _trees[MAX_ROW];
void Init()
{
for (int row = 1; row <= TotRow; row++)
_trees[row].InsertBack(IdRange((row - 1) * TotCol + 1, row * TotCol - 1), TotCol - 1);
for (int row = 1; row <= TotRow; row++)
_trees[0].InsertBack(IdRange(row * TotCol, row * TotCol), 1);
}
IdRange* SplitRange(IdRange& range, int cutLen)
{
IdRange* a = new IdRange[2];
a[0] = IdRange(range.BeginId, range.BeginId + cutLen - 2);
a[1] = IdRange(range.BeginId + cutLen, range.LastId);
return a;
}
bool IsNull(IdRange& range)
{
return range.BeginId > range.LastId;
}
int Solve(int row, int col)
{
if (col == TotCol)
{
//出队
pair<IdRange, int> out = _trees[0].FindKth(row);
//向前看齐
_trees[0].Split(row, 1, SplitRange, IsNull);
//入队
_trees[0].InsertBack(out.first, 1);
return out.first.BeginId;
}
else
{
//出队
pair<IdRange, int> out = _trees[row].FindKth(col);
//向左看齐
_trees[row].Split(col, out.second, SplitRange, IsNull);
pair<IdRange, int> rowBack = _trees[0].FindKth(row);
_trees[row].InsertBack(rowBack.first, 1);
//向前看齐
_trees[0].Split(row, 1, SplitRange, IsNull);
//入队
_trees[0].InsertBack(IdRange(out.first.BeginId + out.second - 1, out.first.BeginId + out.second - 1), 1);
return out.first.BeginId + out.second - 1;
}
}
#undef int
int main()
{
int opCnt;
scanf("%lld%lld%d", &TotRow, &TotCol, &opCnt);
Init();
while (opCnt--)
{
long long row, col;
scanf("%lld%lld", &row, &col);
printf("%lld\n", Solve(row, col));
}
return 0;
}
原文:https://www.cnblogs.com/headboy2002/p/9194081.html