luogu3960 列队

Posted headboy2002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu3960 列队相关的知识,希望对你有一定的参考价值。

题目大意  

  有一个方阵,在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

  1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 x 行第 m列。
  2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 n 行第 m列。
  3. 在每一个离队的学生要归队时,队伍中有且仅有第 n 行 第 m 列一个空位,这时这个学生会自然地填补到这个位置。

  计算每一次离队事件中,离队的同学的编号是多少。n,m,q<=3*10^5。

题解

  直接存储空间太大。经观察发现,一排中有很多同学的编号是连续的,因此我们可以用一个维护(beginId,lastId)的节点来表示多个同学的位置状态。因此我们可以对每一排(不包括一排中位于最右一列的位置)和最后一列各运用一棵SplayTree。

该SplayTree的初步定义

节点维护的是什么?

  每个节点内的值Val(beginId,lastId)表示编号范围为[beginId,lastId]的同学的位置是连续的,这些同学(以后简称同学块)都由该节点维护。

树这个整体维护的是什么?

  拿一排来说,这一排的Splay树内的所有节点所维护的同学块组成了这一排内的所有同学。

树节点排列的关键字是什么?

  拿一排来说,关键字是每个节点所维护的同学块的在排中的位置。

出队入队过程

  如果该同学不在最后一列,则在维护它所在排的SplayTree中输入同学的所在列,得到维护该同学的节点,然后将维护该同学的节点分成两半,一半维护原节点所维护同学块在该同学左面的那部分,一半维护右半部分。随后将最后一列同排的同学从维护最后一列的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
", Solve(row, col));
	}
	return 0;
}

  

 

以上是关于luogu3960 列队的主要内容,如果未能解决你的问题,请参考以下文章

luogu3960 列队

luogu 3960 列队

[Luogu] 列队

P3960 列队

P3960 列队

luoguP3960 [noip2017]列队(树状数组)