zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    java实现httpclient 访问
    推荐博文
    Running With xpi
    1 Spring MVC 原理
    windows服务相关
    求职面试三部曲
    使用mvn插件执行工程单元测试OOM的解决办法
    maven-surefire插件问题
    小问题
    NFA到DFA实例
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9194081.html
Copyright © 2011-2022 走看看