• luogu3939 数颜色


    题目大意

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

      

  • 相关阅读:
    Springboot+mybatis-plus+mysql+clickhouse集成多数据源
    对集合里每个元素是一个对象,按照对象某一个属性值给这个集合排序
    vue的a-tree-select选择父节点回显的是子节点
    Es简单条件查询
    使用Ant Desigen在vue里面实现分页以及表头的模糊查询
    搭建第一个vue项目
    Address localhost:1099 is already in use
    spring的控制反转DI---基于注解实现
    mybatis下的ResultMap配置一对一以及一对多
    mybatis入门
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8993035.html
Copyright © 2020-2023  润新知